From cebab00a027c2b6ad6e36d5dea4e2ac1d2eb99d9 Mon Sep 17 00:00:00 2001 From: Jason Swenski Date: Tue, 15 Dec 2015 01:56:36 -0700 Subject: [PATCH 1/6] Fixing the fact that getInvoice(id,PUBLIC_NO_TOKEN) was actually still signing the requests and therefore not actually using the public API. This doesn't work in some cases because the /invoice endpoint doesn't support some facades. Also refactors some of the parameter building to off-the-shelf apache commons methods --- .gitignore | 8 ++ src/main/java/controller/BitPay.java | 124 +++++++++++---------------- 2 files changed, 59 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index e169f73..f1277e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ +.DS_Store +.classpath +.project +.settings/ +bin/.gitignore +lib/.DS_Store +pom.xml +target/ locals/ *.key java-bitpay-client.xml diff --git a/src/main/java/controller/BitPay.java b/src/main/java/controller/BitPay.java index 2d6c289..11a050e 100644 --- a/src/main/java/controller/BitPay.java +++ b/src/main/java/controller/BitPay.java @@ -14,8 +14,10 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.bitcoinj.core.ECKey; @@ -381,14 +383,13 @@ public class BitPay { */ public Invoice getInvoice(String invoiceId, String token) throws BitPayException { - Hashtable parameters = this.getParams(); - - parameters.put("token", token); - - HttpResponse response = this.get("invoices/" + invoiceId, parameters); + final List params = new ArrayList(); + params.add(new BasicNameValuePair("token", token)); + + boolean requireSignature = !PUBLIC_NO_TOKEN.equals(token); + HttpResponse response = this.get("invoices/" + invoiceId, params, requireSignature); Invoice i; - try { i = new ObjectMapper().readValue(this.responseToJsonString(response), Invoice.class); } catch (JsonProcessingException e) { @@ -409,13 +410,13 @@ public class BitPay { */ public List getInvoices(String dateStart, String dateEnd) throws BitPayException { - Hashtable parameters = this.getParams(); + + final List params = new ArrayList(); + params.add(new BasicNameValuePair("token", this.getAccessToken(FACADE_MERCHANT))); + params.add(new BasicNameValuePair("dateStart", dateStart)); + params.add(new BasicNameValuePair("dateEnd", dateEnd)); - parameters.put("token", this.getAccessToken(FACADE_MERCHANT)); - parameters.put("dateStart", dateStart); - parameters.put("dateEnd", dateEnd); - - HttpResponse response = this.get("invoices", parameters); + HttpResponse response = this.get("invoices", params); List invoices; @@ -510,17 +511,17 @@ public class BitPay { * @throws BitPayException */ public boolean cancelRefundRequest(Invoice invoice, String refundId) throws BitPayException - { - Refund refund = this.getRefund(invoice, refundId); - if (refund == null) - { - throw new BitPayException("Error - refundId is not associated with specified invoice"); - } + { + Refund refund = this.getRefund(invoice, refundId); + if (refund == null) + { + throw new BitPayException("Error - refundId is not associated with specified invoice"); + } - Hashtable parameters = this.getParams(); - parameters.put("token", refund.getToken()); - - HttpResponse response = this.delete("invoices/" + invoice.getId() + "/refunds/" + refundId, parameters); + final List params = new ArrayList(); + params.add(new BasicNameValuePair("token", refund.getToken())); + + HttpResponse response = this.delete("invoices/" + invoice.getId() + "/refunds/" + refundId, params); String result = this.responseToJsonString(response); return (result.equals("\"Success\"")); @@ -536,11 +537,10 @@ public class BitPay { public Refund getRefund(Invoice invoice, String refundId) throws BitPayException { Refund refund = new Refund(); - Hashtable parameters = this.getParams(); - - parameters.put("token", invoice.getToken()); - - HttpResponse response = this.get("invoices/" + invoice.getId() + "/refunds/" + refundId, parameters); + + final List params = new ArrayList(); + params.add(new BasicNameValuePair("token", invoice.getToken())); + HttpResponse response = this.get("invoices/" + invoice.getId() + "/refunds/" + refundId, params); ObjectMapper mapper = new ObjectMapper(); @@ -564,11 +564,10 @@ public class BitPay { public List getAllRefunds(Invoice invoice) throws BitPayException { List refunds; - Hashtable parameters = this.getParams(); - - parameters.put("token", invoice.getToken()); - - HttpResponse response = this.get("invoices/" + invoice.getId() + "/refunds", parameters); + final List params = new ArrayList(); + params.add(new BasicNameValuePair("token", invoice.getToken())); + + HttpResponse response = this.get("invoices/" + invoice.getId() + "/refunds", params); try { refunds = Arrays.asList(new ObjectMapper().readValue(this.responseToJsonString(response), Refund[].class)); @@ -686,23 +685,22 @@ public class BitPay { { this.clearAccessTokenCache(); - Hashtable parameters = this.getParams(); - - parameters.put("id", this.getIdentity()); - - HttpResponse response = this.get("tokens", parameters); + final List params = new ArrayList(); + params.add(new BasicNameValuePair("id", this.getIdentity())); + + HttpResponse response = this.get("tokens", params); _tokenCache = responseToTokenCache(response); return _tokenCache.size(); } - - private Hashtable getParams() - { - return new Hashtable(); + + + private HttpResponse get(String uri, List parameters) throws BitPayException { + return get(uri, parameters, true); } - private HttpResponse get(String uri, Hashtable parameters) throws BitPayException + private HttpResponse get(String uri, List parameters, boolean signatureRequired) throws BitPayException { try { @@ -710,23 +708,14 @@ public class BitPay { HttpGet get = new HttpGet(fullURL); if (parameters != null) { - fullURL += "?"; - - for (String key : parameters.keySet()) { - fullURL += key + "=" + parameters.get(key) + "&"; - } - - fullURL = fullURL.substring(0,fullURL.length() - 1); - - get.setURI(new URI(fullURL)); - - String signature = KeyUtils.sign(_ecKey, fullURL); - - get.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); - get.addHeader("x-accept-version", BITPAY_API_VERSION); - get.addHeader("x-signature", signature); + get.setURI(new URI(fullURL+URLEncodedUtils.format(parameters, "UTF-8"))); + } + if(signatureRequired) { + get.addHeader("x-signature", KeyUtils.sign(_ecKey, fullURL)); get.addHeader("x-identity", KeyUtils.bytesToHex(_ecKey.getPubKey())); } + get.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); + get.addHeader("x-accept-version", BITPAY_API_VERSION); return _httpClient.execute(get); @@ -752,9 +741,7 @@ public class BitPay { post.setEntity(new ByteArrayEntity(json.getBytes("UTF8"))); if (signatureRequired) { - String signature = KeyUtils.sign(_ecKey, _baseUrl + uri + json); - - post.addHeader("x-signature", signature); + post.addHeader("x-signature", KeyUtils.sign(_ecKey, _baseUrl + uri + json)); post.addHeader("x-identity", KeyUtils.bytesToHex(_ecKey.getPubKey())); } @@ -783,7 +770,7 @@ public class BitPay { return this.post(uri, json, true); } - private HttpResponse delete(String uri, Hashtable parameters) throws BitPayException + private HttpResponse delete(String uri, List parameters) throws BitPayException { try { @@ -791,21 +778,12 @@ public class BitPay { HttpDelete delete = new HttpDelete(fullURL); if (parameters != null) { - fullURL += "?"; - - for (String key : parameters.keySet()) { - fullURL += key + "=" + parameters.get(key) + "&"; - } - - fullURL = fullURL.substring(0,fullURL.length() - 1); - - delete.setURI(new URI(fullURL)); - - String signature = KeyUtils.sign(_ecKey, fullURL); + + delete.setURI(new URI(fullURL+URLEncodedUtils.format(parameters, "UTF-8"))); delete.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); delete.addHeader("x-accept-version", BITPAY_API_VERSION); - delete.addHeader("x-signature", signature); + delete.addHeader("x-signature", KeyUtils.sign(_ecKey, fullURL)); delete.addHeader("x-identity", KeyUtils.bytesToHex(_ecKey.getPubKey())); } From 2243bfedd55e0b4c617aa4d7c956b4553c1dbfe7 Mon Sep 17 00:00:00 2001 From: Jason Swenski Date: Tue, 15 Dec 2015 15:16:58 -0700 Subject: [PATCH 2/6] Forgot to add ? in the URL params. A proper solution would use URIBuilder I'll contribute this in a later pull request --- src/main/java/controller/BitPay.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/controller/BitPay.java b/src/main/java/controller/BitPay.java index 11a050e..e26511c 100644 --- a/src/main/java/controller/BitPay.java +++ b/src/main/java/controller/BitPay.java @@ -708,7 +708,9 @@ public class BitPay { HttpGet get = new HttpGet(fullURL); if (parameters != null) { - get.setURI(new URI(fullURL+URLEncodedUtils.format(parameters, "UTF-8"))); + + fullURL += "?" + URLEncodedUtils.format(parameters, "UTF-8"); + get.setURI(new URI(fullURL)); } if(signatureRequired) { get.addHeader("x-signature", KeyUtils.sign(_ecKey, fullURL)); @@ -717,6 +719,7 @@ public class BitPay { get.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); get.addHeader("x-accept-version", BITPAY_API_VERSION); + return _httpClient.execute(get); } catch (URISyntaxException e) { @@ -778,8 +781,10 @@ public class BitPay { HttpDelete delete = new HttpDelete(fullURL); if (parameters != null) { + + fullURL += "?" + URLEncodedUtils.format(parameters, "UTF-8"); - delete.setURI(new URI(fullURL+URLEncodedUtils.format(parameters, "UTF-8"))); + delete.setURI(new URI(fullURL)); delete.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); delete.addHeader("x-accept-version", BITPAY_API_VERSION); From aea9ea062d55536db0a44deb7c04266202520e47 Mon Sep 17 00:00:00 2001 From: Mike Rosseel Date: Fri, 8 Jan 2016 23:09:54 +0100 Subject: [PATCH 3/6] added maven pom file, moved files into maven dir structure, small changes due to bitcoinj 13 --- src/main/java/controller/BitPay.java | 14 ++++++-------- src/main/java/controller/KeyUtils.java | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/java/controller/BitPay.java b/src/main/java/controller/BitPay.java index e26511c..187cb2a 100644 --- a/src/main/java/controller/BitPay.java +++ b/src/main/java/controller/BitPay.java @@ -21,14 +21,12 @@ import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.bitcoinj.core.ECKey; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Hashtable; -import java.util.List; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.bitcoinj.core.ECKey; public class BitPay { diff --git a/src/main/java/controller/KeyUtils.java b/src/main/java/controller/KeyUtils.java index 9baf2b8..593966f 100644 --- a/src/main/java/controller/KeyUtils.java +++ b/src/main/java/controller/KeyUtils.java @@ -11,16 +11,21 @@ import java.net.URI; import java.net.URISyntaxException; import java.security.SecureRandom; +import org.bitcoinj.core.Base58; +import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.ECKey.ECDSASignature; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.Utils; + public class KeyUtils { final private static char[] hexArray = "0123456789abcdef".toCharArray(); final private static String PRIV_KEY_FILENAME = "bitpay_private.key"; - private static URI privateKey; - public KeyUtils() { - } + public KeyUtils() {} - public static boolean privateKeyExists() { + public static boolean privateKeyExists() + { return new File(PRIV_KEY_FILENAME).exists(); } @@ -143,6 +148,9 @@ public class KeyUtils { return Base58.encode(unencodedBytes); } + return encoded; + } + public static String sign(ECKey key, String input) throws UnsupportedEncodingException { byte[] data = input.getBytes("UTF8"); @@ -154,8 +162,9 @@ public class KeyUtils { return bytesToHex(bytes); } - private static int getHexVal(char hex) { - int val = (int) hex; + private static int getHexVal(char hex) + { + int val = (int)hex; return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); } From c5ba2e50c5695a9ef4f1b9f80871f62ee5722b4a Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Tue, 22 Mar 2016 16:06:46 -0400 Subject: [PATCH 4/6] Conversion to Maven, Tests adjusted to help developers - As per mrosseel's pom file, conversion of all dependencies to use maven2. This will ease the pain of managing a bunch of jar files in lib - Repaired some deprecation warnings in bitcoinj dependency around Sha256 creation and ECKey construction. - Adjusted test harness to create tokens for pos/merchant facade, this is done once the first time someone runs the tests. The test runner just has to pair with their test account. The tester is does not have cascading test failures that might confuses as to what he/she needs to do. - Added some notes about how to do the refund test. --- src/main/java/controller/BitPay.java | 14 +++++++------ src/main/java/controller/KeyUtils.java | 25 ++++++++++------------ src/test/java/test/BitPayTest.java | 29 +++++++------------------- src/test/java/test/BitPayTest2.java | 8 ++++--- 4 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/main/java/controller/BitPay.java b/src/main/java/controller/BitPay.java index 187cb2a..e26511c 100644 --- a/src/main/java/controller/BitPay.java +++ b/src/main/java/controller/BitPay.java @@ -21,12 +21,14 @@ import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.bitcoinj.core.ECKey; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.bitcoinj.core.ECKey; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; public class BitPay { diff --git a/src/main/java/controller/KeyUtils.java b/src/main/java/controller/KeyUtils.java index 593966f..bd6605c 100644 --- a/src/main/java/controller/KeyUtils.java +++ b/src/main/java/controller/KeyUtils.java @@ -7,16 +7,8 @@ import org.bitcoinj.core.Sha256Hash; import java.io.*; import java.math.BigInteger; -import java.net.URI; -import java.net.URISyntaxException; import java.security.SecureRandom; -import org.bitcoinj.core.Base58; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.ECKey.ECDSASignature; -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Utils; - public class KeyUtils { final private static char[] hexArray = "0123456789abcdef".toCharArray(); @@ -29,16 +21,20 @@ public class KeyUtils { return new File(PRIV_KEY_FILENAME).exists(); } - public static ECKey createEcKey() { + public static ECKey createEcKey() + { //Default constructor uses SecureRandom numbers. return new ECKey(); } - public static ECKey createEcKeyFromHexString(String privateKey) { + public static ECKey createEcKeyFromHexString(String privateKey) + { //if you are going to choose this option, please ensure this string is as random as //possible, consider http://world.std.com/~reinhold/diceware.html SecureRandom randomSeed = new SecureRandom(privateKey.getBytes()); - return new ECKey(randomSeed); + ECKey key = new ECKey(randomSeed); + + return key; } /** @@ -48,9 +44,10 @@ public class KeyUtils { return createEcKeyFromHexString(getKeyStringFromFile(privKeyFile)); } - public static ECKey loadEcKey() throws IOException { - FileInputStream fileInputStream; - File file; + public static ECKey loadEcKey() throws IOException + { + FileInputStream fileInputStream = null; + File file = new File(PRIV_KEY_FILENAME); if (KeyUtils.privateKey == null) { file = new File(PRIV_KEY_FILENAME); diff --git a/src/test/java/test/BitPayTest.java b/src/test/java/test/BitPayTest.java index d4719f1..7153b1f 100644 --- a/src/test/java/test/BitPayTest.java +++ b/src/test/java/test/BitPayTest.java @@ -9,9 +9,6 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @@ -27,33 +24,21 @@ public class BitPayTest { private static String clientName = "BitPay Java Library Tester"; private static String pairingCode; - private static String refundInvoiceId = null; - private static URI myKeyFile; + private static String refundInvoiceId; @Before - public void setUp() throws BitPayException, IOException, URISyntaxException { + public void setUp() throws BitPayException { //ensure the second argument (api url) is the same as the one used in setUpOneTime() - bitpay = new BitPay(myKeyFile, clientName, BitPay.BITPAY_TEST_URL); + bitpay = new BitPay(clientName, BitPay.BITPAY_TEST_URL); } @BeforeClass - public static void setUpOneTime() throws InterruptedException, IOException, BitPayException, URISyntaxException { + public static void setUpOneTime() throws Exception + { boolean dumpOut = false; - - //create a key, if a file does exist at the uri, myKeyfile, a new key will be created in the construction of the client - //ECKey myKey = KeyUtils.createEcKey(); - - - myKeyFile = new URI("file:///tmp/bitpay_private.key"); //if file exists, it will not overwrite - - //save the somewhere that you can reuse it: - //this saves a EC key to compressed ASN.1 DER encoded format - //if you use your own key (not generated by our key utils), then ensure your key is in the above format - //KeyUtils.saveEcKey(myKey, myKeyFile); - // This scenario qualifies that this (test) client does not have merchant facade access. clientName += " on " + java.net.InetAddress.getLocalHost(); - BitPay bitpay = new BitPay(myKeyFile, clientName, BitPay.BITPAY_TEST_URL); + BitPay bitpay = new BitPay(clientName, BitPay.BITPAY_TEST_URL); // Authorize this client for use with a BitPay merchant account. This client requires both // POS and MERCHANT facades. @@ -77,7 +62,7 @@ public class BitPayTest { System.out.println("Info: Client is requesting POS facade access. Pair this client with your merchant account using the pairing code: " + pairingCode); dumpOut = true; //we already failed to authorize for a POS token, therefore we must sleep a bit to try to authorize for any other facade (rate limiter on the api side) - Thread.sleep(10000); + Thread.sleep(3000); } if (!bitpay.clientIsAuthorized(BitPay.FACADE_MERCHANT)) diff --git a/src/test/java/test/BitPayTest2.java b/src/test/java/test/BitPayTest2.java index 5abdeda..dea1e8a 100644 --- a/src/test/java/test/BitPayTest2.java +++ b/src/test/java/test/BitPayTest2.java @@ -7,7 +7,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import java.net.UnknownHostException; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull; @@ -23,14 +23,14 @@ public class BitPayTest2 { } @BeforeClass - public static void setUpOneTime() throws UnknownHostException, BitPayException + public static void setUpOneTime() throws Exception { // If this test has never been run before then this test must be run twice in order to pass. // The first time this test runs it will create an identity and emit a client pairing code. // The pairing code must then be authorized in a BitPay account. Running the test a second // time should result in the authorized client (this test) running to completion. clientName += " on " + java.net.InetAddress.getLocalHost(); - BitPay bitpay = new BitPay(clientName, BitPay.BITPAY_TEST_URL); //this tests the old way of creating keys/clients + BitPay bitpay = new BitPay(clientName, BitPay.BITPAY_TEST_URL); if (!bitpay.clientIsAuthorized(BitPay.FACADE_POS)) { @@ -55,6 +55,8 @@ public class BitPayTest2 { } catch (BitPayException e) { e.printStackTrace(); } + System.out.println(invoice.getId()); + refundInvoiceId = invoice.getId(); assertNotNull(invoice.getId()); } } From 3bc1178a78e4beed4dff2246632824dc78309522 Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Thu, 24 Mar 2016 17:47:35 -0400 Subject: [PATCH 5/6] Creating BitPay client with URI of key - Added constructors for passing the URI of your private key. We prefer to have users of this library pass in a URI for their key file. That way, this api isn't just writing new key files to the current working directory. If the URI does not have a key already, one will be created there (if writeable). It will be the user's responsibility to safeguard their private keys. - A bit of code cleanup in terms of spelling and removal of warnings, etc. --- src/main/java/controller/KeyUtils.java | 34 +++++++++++--------------- src/test/java/test/BitPayTest.java | 29 ++++++++++++++++------ src/test/java/test/BitPayTest2.java | 8 +++--- 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/main/java/controller/KeyUtils.java b/src/main/java/controller/KeyUtils.java index bd6605c..9baf2b8 100644 --- a/src/main/java/controller/KeyUtils.java +++ b/src/main/java/controller/KeyUtils.java @@ -7,34 +7,33 @@ import org.bitcoinj.core.Sha256Hash; import java.io.*; import java.math.BigInteger; +import java.net.URI; +import java.net.URISyntaxException; import java.security.SecureRandom; public class KeyUtils { final private static char[] hexArray = "0123456789abcdef".toCharArray(); final private static String PRIV_KEY_FILENAME = "bitpay_private.key"; + private static URI privateKey; - public KeyUtils() {} + public KeyUtils() { + } - public static boolean privateKeyExists() - { + public static boolean privateKeyExists() { return new File(PRIV_KEY_FILENAME).exists(); } - public static ECKey createEcKey() - { + public static ECKey createEcKey() { //Default constructor uses SecureRandom numbers. return new ECKey(); } - public static ECKey createEcKeyFromHexString(String privateKey) - { + public static ECKey createEcKeyFromHexString(String privateKey) { //if you are going to choose this option, please ensure this string is as random as //possible, consider http://world.std.com/~reinhold/diceware.html SecureRandom randomSeed = new SecureRandom(privateKey.getBytes()); - ECKey key = new ECKey(randomSeed); - - return key; + return new ECKey(randomSeed); } /** @@ -44,10 +43,9 @@ public class KeyUtils { return createEcKeyFromHexString(getKeyStringFromFile(privKeyFile)); } - public static ECKey loadEcKey() throws IOException - { - FileInputStream fileInputStream = null; - File file = new File(PRIV_KEY_FILENAME); + public static ECKey loadEcKey() throws IOException { + FileInputStream fileInputStream; + File file; if (KeyUtils.privateKey == null) { file = new File(PRIV_KEY_FILENAME); @@ -145,9 +143,6 @@ public class KeyUtils { return Base58.encode(unencodedBytes); } - return encoded; - } - public static String sign(ECKey key, String input) throws UnsupportedEncodingException { byte[] data = input.getBytes("UTF8"); @@ -159,9 +154,8 @@ public class KeyUtils { return bytesToHex(bytes); } - private static int getHexVal(char hex) - { - int val = (int)hex; + private static int getHexVal(char hex) { + int val = (int) hex; return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); } diff --git a/src/test/java/test/BitPayTest.java b/src/test/java/test/BitPayTest.java index 7153b1f..d4719f1 100644 --- a/src/test/java/test/BitPayTest.java +++ b/src/test/java/test/BitPayTest.java @@ -9,6 +9,9 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @@ -24,21 +27,33 @@ public class BitPayTest { private static String clientName = "BitPay Java Library Tester"; private static String pairingCode; - private static String refundInvoiceId; + private static String refundInvoiceId = null; + private static URI myKeyFile; @Before - public void setUp() throws BitPayException { + public void setUp() throws BitPayException, IOException, URISyntaxException { //ensure the second argument (api url) is the same as the one used in setUpOneTime() - bitpay = new BitPay(clientName, BitPay.BITPAY_TEST_URL); + bitpay = new BitPay(myKeyFile, clientName, BitPay.BITPAY_TEST_URL); } @BeforeClass - public static void setUpOneTime() throws Exception - { + public static void setUpOneTime() throws InterruptedException, IOException, BitPayException, URISyntaxException { boolean dumpOut = false; + + //create a key, if a file does exist at the uri, myKeyfile, a new key will be created in the construction of the client + //ECKey myKey = KeyUtils.createEcKey(); + + + myKeyFile = new URI("file:///tmp/bitpay_private.key"); //if file exists, it will not overwrite + + //save the somewhere that you can reuse it: + //this saves a EC key to compressed ASN.1 DER encoded format + //if you use your own key (not generated by our key utils), then ensure your key is in the above format + //KeyUtils.saveEcKey(myKey, myKeyFile); + // This scenario qualifies that this (test) client does not have merchant facade access. clientName += " on " + java.net.InetAddress.getLocalHost(); - BitPay bitpay = new BitPay(clientName, BitPay.BITPAY_TEST_URL); + BitPay bitpay = new BitPay(myKeyFile, clientName, BitPay.BITPAY_TEST_URL); // Authorize this client for use with a BitPay merchant account. This client requires both // POS and MERCHANT facades. @@ -62,7 +77,7 @@ public class BitPayTest { System.out.println("Info: Client is requesting POS facade access. Pair this client with your merchant account using the pairing code: " + pairingCode); dumpOut = true; //we already failed to authorize for a POS token, therefore we must sleep a bit to try to authorize for any other facade (rate limiter on the api side) - Thread.sleep(3000); + Thread.sleep(10000); } if (!bitpay.clientIsAuthorized(BitPay.FACADE_MERCHANT)) diff --git a/src/test/java/test/BitPayTest2.java b/src/test/java/test/BitPayTest2.java index dea1e8a..333a5b8 100644 --- a/src/test/java/test/BitPayTest2.java +++ b/src/test/java/test/BitPayTest2.java @@ -7,6 +7,8 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.net.UnknownHostException; + import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull; @@ -23,14 +25,14 @@ public class BitPayTest2 { } @BeforeClass - public static void setUpOneTime() throws Exception + public static void setUpOneTime() throws UnknownHostException, BitPayException { // If this test has never been run before then this test must be run twice in order to pass. // The first time this test runs it will create an identity and emit a client pairing code. // The pairing code must then be authorized in a BitPay account. Running the test a second // time should result in the authorized client (this test) running to completion. clientName += " on " + java.net.InetAddress.getLocalHost(); - BitPay bitpay = new BitPay(clientName, BitPay.BITPAY_TEST_URL); + BitPay bitpay = new BitPay(clientName, BitPay.BITPAY_TEST_URL); //this tests the old way of creating keys/clients if (!bitpay.clientIsAuthorized(BitPay.FACADE_POS)) { @@ -55,8 +57,6 @@ public class BitPayTest2 { } catch (BitPayException e) { e.printStackTrace(); } - System.out.println(invoice.getId()); - refundInvoiceId = invoice.getId(); assertNotNull(invoice.getId()); } } From ae7f1392b5248d0cd31ecd55b601b2630c3ea09d Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Mon, 4 Apr 2016 14:24:43 -0400 Subject: [PATCH 6/6] Refund test improvements / Signature not required for rate estimates --- .gitignore | 10 +- java-bitpay-client.iml | 38 +++++ src/main/java/controller/BitPay.java | 228 +++++++++++++-------------- src/test/java/test/BitPayTest.java | 28 +++- src/test/java/test/BitPayTest2.java | 2 - 5 files changed, 171 insertions(+), 135 deletions(-) create mode 100644 java-bitpay-client.iml diff --git a/.gitignore b/.gitignore index f1277e0..fc43385 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,3 @@ -.DS_Store -.classpath -.project -.settings/ -bin/.gitignore -lib/.DS_Store -pom.xml -target/ locals/ *.key java-bitpay-client.xml @@ -13,3 +5,5 @@ java-bitpay-client.properties untitled folder/ .idea/ target +out +lib diff --git a/java-bitpay-client.iml b/java-bitpay-client.iml new file mode 100644 index 0000000..390985a --- /dev/null +++ b/java-bitpay-client.iml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/controller/BitPay.java b/src/main/java/controller/BitPay.java index e26511c..e40b1c5 100644 --- a/src/main/java/controller/BitPay.java +++ b/src/main/java/controller/BitPay.java @@ -26,6 +26,7 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.List; @@ -53,13 +54,13 @@ public class BitPay { /** * Constructor for use if the keys and SIN are managed by this library. + * * @param clientName The label for this client. - * @param envUrl The target server URL. + * @param envUrl The target server URL. * @throws BitPayException */ @Deprecated - public BitPay(String clientName, String envUrl) throws BitPayException - { + public BitPay(String clientName, String envUrl) throws BitPayException { if (clientName.equals(BITPAY_PLUGIN_INFO)) { try { clientName += " on " + java.net.InetAddress.getLocalHost(); @@ -94,27 +95,28 @@ public class BitPay { /** * Constructor for use if the keys and SIN are managed by this library. Use BitPay production server. + * * @param clientName The label for this client. * @throws BitPayException */ @Deprecated - public BitPay(String clientName) throws BitPayException - { + public BitPay(String clientName) throws BitPayException { this(clientName, BITPAY_URL); } /** * Constructor for use if the keys and SIN are managed by this library. Use BitPay production server. Default client name. + * * @throws BitPayException */ @Deprecated - public BitPay() throws BitPayException - { + public BitPay() throws BitPayException { this(BITPAY_PLUGIN_INFO, BITPAY_URL); } /** * Constructor for use if the keys derived external to this library. Use BitPay production server. Default client name. + * * @param privateKey A URI object representing the compressed, DER-encoded ASN.1 private key * @throws BitPayException, IOException */ @@ -124,6 +126,7 @@ public class BitPay { /** * Constructor for use if the keys derived external to this library. Use BitPay production server. Default client name. + * * @param privateKey A URI object representing the compressed, DER-encoded ASN.1 private key * @param clientName The label for this client. * @throws BitPayException, IOException @@ -134,9 +137,10 @@ public class BitPay { /** * Constructor for use if the keys derived external to this library. Use BitPay production server. Default client name. + * * @param privateKey A URI object representing the compressed, DER-encoded ASN.1 private key * @param clientName The label for this client. - * @param envUrl The target server URL. + * @param envUrl The target server URL. * @throws BitPayException, IOException */ public BitPay(URI privateKey, String clientName, String envUrl) throws BitPayException, IOException, URISyntaxException { @@ -145,13 +149,13 @@ public class BitPay { /** * Constructor for use if the keys and SIN were derived external to this library. - * @param ecKey An elliptical curve key. + * + * @param ecKey An elliptical curve key. * @param clientName The label for this client. - * @param envUrl The target server URL. + * @param envUrl The target server URL. * @throws BitPayException */ - public BitPay(ECKey ecKey, String clientName, String envUrl) throws BitPayException - { + public BitPay(ECKey ecKey, String clientName, String envUrl) throws BitPayException { _ecKey = ecKey; this.deriveIdentity(); @@ -180,41 +184,41 @@ public class BitPay { /** * Constructor for use if the keys and SIN were derived external to this library. Use BitPay production server. - * @param ecKey An elliptical curve key. + * + * @param ecKey An elliptical curve key. * @param clientName The label for this client. * @throws BitPayException */ - public BitPay(ECKey ecKey, String clientName) throws BitPayException - { + public BitPay(ECKey ecKey, String clientName) throws BitPayException { this(ecKey, clientName, BITPAY_URL); } /** * Constructor for use if the keys and SIN were derived external to this library. Use BitPay production server. Default client name. + * * @param ecKey An elliptical curve key. * @throws BitPayException */ - public BitPay(ECKey ecKey) throws BitPayException - { + public BitPay(ECKey ecKey) throws BitPayException { this(ecKey, BITPAY_PLUGIN_INFO, BITPAY_URL); } /** * Get the generated client identity. + * * @return Client identify as a string */ - public String getIdentity() - { + public String getIdentity() { return _identity; } /** * Authorize this client for use with the BitPay server. + * * @param pairingCode A pairing code generated at https://bitpay.com/dashboard/merchant/api-tokens. * @throws BitPayException */ - public void authorizeClient(String pairingCode) throws BitPayException - { + public void authorizeClient(String pairingCode) throws BitPayException { Token token = new Token(); token.setId(_identity); token.setGuid(this.getGuid()); @@ -250,12 +254,12 @@ public class BitPay { /** * Request a pairing code from the BitPay server. + * * @param facade Defines the level of API access being requested * @return A pairing code for claim at https://bitpay.com/dashboard/merchant/api-tokens. * @throws BitPayException */ - public String requestClientAuthorization(String facade) throws BitPayException - { + public String requestClientAuthorization(String facade) throws BitPayException { Token token = new Token(); token.setId(_identity); token.setGuid(this.getGuid()); @@ -298,33 +302,33 @@ public class BitPay { /** * Test whether this client is authorized for a specified level of API access. + * * @param facade Defines the level of API access being requested. * @return True if this client is authorized, false otherwise. */ - public boolean clientIsAuthorized(String facade) - { + public boolean clientIsAuthorized(String facade) { return _tokenCache.containsKey(facade); } /** * Retrieve a token associated with a known resource. The token is used to access other related resources. + * * @param id The identifier for the desired resource. * @return The token associated with resource. */ - public String getAccessToken(String id) - { + public String getAccessToken(String id) { return _tokenCache.get(id); } /** * Create a BitPay invoice. + * * @param invoice An Invoice object with request parameters defined. - * @param token The resource access token for the request. + * @param token The resource access token for the request. * @return A BitPay generated Invoice object. * @throws BitPayException */ - public Invoice createInvoice(Invoice invoice, String token) throws BitPayException - { + public Invoice createInvoice(Invoice invoice, String token) throws BitPayException { invoice.setToken(token); invoice.setGuid(this.getGuid()); @@ -354,38 +358,38 @@ public class BitPay { /** * Create a BitPay invoice using the POS facade. + * * @param invoice An Invoice object with request parameters defined. * @return A BitPay generated Invoice object. * @throws BitPayException */ - public Invoice createInvoice(Invoice invoice) throws BitPayException - { + public Invoice createInvoice(Invoice invoice) throws BitPayException { return this.createInvoice(invoice, this.getAccessToken(FACADE_POS)); } /** * Retrieve a BitPay invoice by invoice id using the public facade. + * * @param invoiceId The id of the invoice to retrieve. * @return A BitPay Invoice object. * @throws BitPayException */ - public Invoice getInvoice(String invoiceId) throws BitPayException - { - return this.getInvoice(invoiceId, PUBLIC_NO_TOKEN); + public Invoice getInvoice(String invoiceId) throws BitPayException { + return this.getInvoice(invoiceId, PUBLIC_NO_TOKEN); } /** * Retrieve a BitPay invoice by invoice id using the specified facade. The client must have been previously authorized for the specified facade (the public facade requires no authorization). + * * @param invoiceId The id of the invoice to retrieve. - * @param token The facade/invoice token (e.g., pos/invoice) for the invoice. + * @param token The facade/invoice token (e.g., pos/invoice) for the invoice. * @return A BitPay Invoice object. * @throws BitPayException */ - public Invoice getInvoice(String invoiceId, String token) throws BitPayException - { + public Invoice getInvoice(String invoiceId, String token) throws BitPayException { final List params = new ArrayList(); params.add(new BasicNameValuePair("token", token)); - + boolean requireSignature = !PUBLIC_NO_TOKEN.equals(token); HttpResponse response = this.get("invoices/" + invoiceId, params, requireSignature); @@ -403,14 +407,14 @@ public class BitPay { /** * Retrieve a collection of BitPay invoices. + * * @param dateStart The first date for the query filter. - * @param dateEnd The last date for the query filter. + * @param dateEnd The last date for the query filter. * @return A list of BitPay Invoice objects. * @throws BitPayException */ - public List getInvoices(String dateStart, String dateEnd) throws BitPayException - { - + public List getInvoices(String dateStart, String dateEnd) throws BitPayException { + final List params = new ArrayList(); params.add(new BasicNameValuePair("token", this.getAccessToken(FACADE_MERCHANT))); params.add(new BasicNameValuePair("dateStart", dateStart)); @@ -433,38 +437,38 @@ public class BitPay { /** * Request a full refund for a BitPay invoice. The invoice full price and currency type are used in the request. - * @param invoiceId The id of the BitPay invoice for which a refund request should be made. + * + * @param invoiceId The id of the BitPay invoice for which a refund request should be made. * @param bitcoinAddress The bitcoin address to which the refund should will be made. If left empty ("") and the invoice contains a refund address then the request may success, otherwise it will fail. * @return A BitPay RefundRequest object with the new Refund object. * @throws BitPayException */ - public RefundHelper requestRefund(String invoiceId, String bitcoinAddress) throws BitPayException - { - Invoice invoice = this.getInvoice(invoiceId, this.getAccessToken(FACADE_MERCHANT)); - return this.requestRefund(invoice, bitcoinAddress, invoice.getPrice(), invoice.getCurrency()); + public RefundHelper requestRefund(String invoiceId, String bitcoinAddress) throws BitPayException { + Invoice invoice = this.getInvoice(invoiceId, this.getAccessToken(FACADE_MERCHANT)); + return this.requestRefund(invoice, bitcoinAddress, invoice.getPrice(), invoice.getCurrency()); } /** * Request a refund for a BitPay invoice. - * @param invoice A BitPay invoice object for which a refund request should be made. Must have been obtained using the merchant facade. + * + * @param invoice A BitPay invoice object for which a refund request should be made. Must have been obtained using the merchant facade. * @param bitcoinAddress The bitcoin address to which the refund should will be made. If left empty ("") and the invoice contains a refund address then the request may success, otherwise it will fail. - * @param amount The amount of money to refund. If zero then a request for 100% of the invoice value is created. - * @param currency The three digit currency code specifying the exchange rate to use when calculating the refund bitcoin amount. If this value is "BTC" then no exchange rate calculation is performed. + * @param amount The amount of money to refund. If zero then a request for 100% of the invoice value is created. + * @param currency The three digit currency code specifying the exchange rate to use when calculating the refund bitcoin amount. If this value is "BTC" then no exchange rate calculation is performed. * @return A BitPay RefundRequest object with the new Refund object. * @throws BitPayException */ - public RefundHelper requestRefund(Invoice invoice, String bitcoinAddress, Double amount, String currency) throws BitPayException - { - if (bitcoinAddress == null && !invoice.getFlags().getRefundable()) { + public RefundHelper requestRefund(Invoice invoice, String bitcoinAddress, Double amount, String currency) throws BitPayException { + if (bitcoinAddress == null && !invoice.getFlags().getRefundable()) { throw new BitPayException("Error - cannot refund an invoice without a refund address"); - } + } Refund refund = new Refund(); - refund.setToken(invoice.getToken()); + refund.setToken(invoice.getToken()); refund.setGuid(this.getGuid()); - refund.setAmount(amount); - refund.setBitcoinAddress(bitcoinAddress); - refund.setCurrency(currency); + refund.setAmount(amount); + refund.setBitcoinAddress(bitcoinAddress); + refund.setCurrency(currency); ObjectMapper mapper = new ObjectMapper(); @@ -492,35 +496,34 @@ public class BitPay { /** * Cancel a previously submitted refund request on a BitPay invoice. + * * @param invoiceId The BitPay invoiceId having the associated refund to be canceled. - * @param refundId The refund id for the refund to be canceled. + * @param refundId The refund id for the refund to be canceled. * @return True if the refund was successfully canceled, false otherwise. * @throws BitPayException */ - public boolean cancelRefundRequest(String invoiceId, String refundId) throws BitPayException - { - Invoice invoice = this.getInvoice(invoiceId, this.getAccessToken(FACADE_MERCHANT)); - return this.cancelRefundRequest(invoice, refundId); + public boolean cancelRefundRequest(String invoiceId, String refundId) throws BitPayException { + Invoice invoice = this.getInvoice(invoiceId, this.getAccessToken(FACADE_MERCHANT)); + return this.cancelRefundRequest(invoice, refundId); } /** * Cancel a previously submitted refund request on a BitPay invoice. - * @param invoice The BitPay invoice having the associated refund to be canceled. Must have been obtained using the merchant facade. + * + * @param invoice The BitPay invoice having the associated refund to be canceled. Must have been obtained using the merchant facade. * @param refundId The refund id for the refund to be canceled. * @return True if the refund was successfully canceled, false otherwise. * @throws BitPayException */ - public boolean cancelRefundRequest(Invoice invoice, String refundId) throws BitPayException - { + public boolean cancelRefundRequest(Invoice invoice, String refundId) throws BitPayException { Refund refund = this.getRefund(invoice, refundId); - if (refund == null) - { + if (refund == null) { throw new BitPayException("Error - refundId is not associated with specified invoice"); } final List params = new ArrayList(); params.add(new BasicNameValuePair("token", refund.getToken())); - + HttpResponse response = this.delete("invoices/" + invoice.getId() + "/refunds/" + refundId, params); String result = this.responseToJsonString(response); @@ -529,15 +532,15 @@ public class BitPay { /** * Retrieve a previously made refund request on a BitPay invoice. - * @param invoice The BitPay invoice having the associated refund. + * + * @param invoice The BitPay invoice having the associated refund. * @param refundId The refund id for the refund to be updated with new status. * @return A BitPay invoice object with the associated Refund object updated. * @throws BitPayException */ - public Refund getRefund(Invoice invoice, String refundId) throws BitPayException - { + public Refund getRefund(Invoice invoice, String refundId) throws BitPayException { Refund refund = new Refund(); - + final List params = new ArrayList(); params.add(new BasicNameValuePair("token", invoice.getToken())); HttpResponse response = this.get("invoices/" + invoice.getId() + "/refunds/" + refundId, params); @@ -557,16 +560,16 @@ public class BitPay { /** * Retrieve all refund requests on a BitPay invoice. + * * @param invoice The BitPay invoice object having the associated refunds. * @return A BitPay invoice object with the associated Refund objects updated. * @throws BitPayException */ - public List getAllRefunds(Invoice invoice) throws BitPayException - { + public List getAllRefunds(Invoice invoice) throws BitPayException { List refunds; final List params = new ArrayList(); params.add(new BasicNameValuePair("token", invoice.getToken())); - + HttpResponse response = this.get("invoices/" + invoice.getId() + "/refunds", params); try { @@ -582,11 +585,11 @@ public class BitPay { /** * Retrieve the exchange rate table maintained by BitPay. See https://bitpay.com/bitcoin-exchange-rates. + * * @return A Rates object populated with the BitPay exchange rate table. * @throws BitPayException */ - public Rates getRates() throws BitPayException - { + public Rates getRates() throws BitPayException { HttpResponse response = this.get("rates"); List rates; @@ -619,14 +622,12 @@ public class BitPay { } } - private void deriveIdentity() throws IllegalArgumentException - { + private void deriveIdentity() throws IllegalArgumentException { // Identity in this implementation is defined to be the SIN. _identity = KeyUtils.deriveSIN(_ecKey); } - private Hashtable responseToTokenCache(HttpResponse response) throws BitPayException - { + private Hashtable responseToTokenCache(HttpResponse response) throws BitPayException { // The response is expected to be an array of key/value pairs (facade name = token). String json = this.responseToJsonString(response); @@ -636,7 +637,8 @@ public class BitPay { json = json.replaceAll("\\]", ""); json = json.replaceAll("\\},\\{", ","); if (json.length() > 0) { - _tokenCache = new ObjectMapper().readValue(json, new TypeReference>(){}); + _tokenCache = new ObjectMapper().readValue(json, new TypeReference>() { + }); } } catch (JsonProcessingException e) { @@ -648,18 +650,15 @@ public class BitPay { return _tokenCache; } - private void clearAccessTokenCache() - { + private void clearAccessTokenCache() { _tokenCache = new Hashtable(); } - private void cacheAccessToken(String id, String token) - { + private void cacheAccessToken(String id, String token) { _tokenCache.put(id, token); } - private boolean tryGetAccessTokens() throws BitPayException - { + private boolean tryGetAccessTokens() throws BitPayException { // Attempt to get access tokens for this client identity. try { @@ -681,45 +680,43 @@ public class BitPay { } } - private int getAccessTokens() throws BitPayException - { + private int getAccessTokens() throws BitPayException { this.clearAccessTokenCache(); final List params = new ArrayList(); params.add(new BasicNameValuePair("id", this.getIdentity())); - + HttpResponse response = this.get("tokens", params); _tokenCache = responseToTokenCache(response); return _tokenCache.size(); } - - + + private HttpResponse get(String uri, List parameters) throws BitPayException { return get(uri, parameters, true); } - private HttpResponse get(String uri, List parameters, boolean signatureRequired) throws BitPayException - { + private HttpResponse get(String uri, List parameters, boolean signatureRequired) throws BitPayException { try { String fullURL = _baseUrl + uri; HttpGet get = new HttpGet(fullURL); if (parameters != null) { - + fullURL += "?" + URLEncodedUtils.format(parameters, "UTF-8"); get.setURI(new URI(fullURL)); } - if(signatureRequired) { + if (signatureRequired) { get.addHeader("x-signature", KeyUtils.sign(_ecKey, fullURL)); get.addHeader("x-identity", KeyUtils.bytesToHex(_ecKey.getPubKey())); } get.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); get.addHeader("x-accept-version", BITPAY_API_VERSION); - + return _httpClient.execute(get); } catch (URISyntaxException e) { @@ -731,13 +728,11 @@ public class BitPay { } } - private HttpResponse get(String uri) throws BitPayException - { - return this.get(uri, null); + private HttpResponse get(String uri) throws BitPayException { + return this.get(uri, null, false); } - private HttpResponse post(String uri, String json, boolean signatureRequired) throws BitPayException - { + private HttpResponse post(String uri, String json, boolean signatureRequired) throws BitPayException { try { HttpPost post = new HttpPost(_baseUrl + uri); @@ -750,7 +745,7 @@ public class BitPay { post.addHeader("x-accept-version", BITPAY_API_VERSION); post.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); - post.addHeader("Content-Type","application/json"); + post.addHeader("Content-Type", "application/json"); return _httpClient.execute(post); @@ -763,27 +758,24 @@ public class BitPay { } } - private HttpResponse post(String uri, String json) throws BitPayException - { + private HttpResponse post(String uri, String json) throws BitPayException { return this.post(uri, json, false); } - private HttpResponse postWithSignature(String uri, String json) throws BitPayException - { + private HttpResponse postWithSignature(String uri, String json) throws BitPayException { return this.post(uri, json, true); } - private HttpResponse delete(String uri, List parameters) throws BitPayException - { + private HttpResponse delete(String uri, List parameters) throws BitPayException { try { String fullURL = _baseUrl + uri; HttpDelete delete = new HttpDelete(fullURL); if (parameters != null) { - + fullURL += "?" + URLEncodedUtils.format(parameters, "UTF-8"); - + delete.setURI(new URI(fullURL)); delete.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); @@ -803,8 +795,7 @@ public class BitPay { } } - private String responseToJsonString(HttpResponse response) throws BitPayException - { + private String responseToJsonString(HttpResponse response) throws BitPayException { if (response == null) { throw new BitPayException("Error: HTTP response is null"); } @@ -857,11 +848,10 @@ public class BitPay { } } - private String getGuid() - { + private String getGuid() { int Min = 0; int Max = 99999999; - return Min + (int)(Math.random() * ((Max - Min) + 1)) + ""; + return Min + (int) (Math.random() * ((Max - Min) + 1)) + ""; } } diff --git a/src/test/java/test/BitPayTest.java b/src/test/java/test/BitPayTest.java index d4719f1..c8662f7 100644 --- a/src/test/java/test/BitPayTest.java +++ b/src/test/java/test/BitPayTest.java @@ -324,16 +324,32 @@ public class BitPayTest { /* To use this test: - 1. use the testShouldGetInvoiceId to generate an invoice - 2. manually pay the invoice with testnet coins, the invoice Id get printed to the console - 3. wait for 6 confirmations (about 1 hour) - 4. run the test again - 5. the refund should happen from the api standpoint, but if on testnet/test.bitpay.com, then the actual refund won't - happen unless you contact support and ask them to run refunds for test.bitpay.com. + You must have a paid/completed invoice in your account (list of invoices) -and- + have enough bitcoin to do the refund. The test looks for the first invoice in the "complete" + state and attempts to refund this, but does not actually refund the invoice. Instead it cancels the refund request. + This means you can re-use the same paid invoice again and again. */ @Test public void testShouldCreateAndCancelRefundRequest() { + List invoices; + try { + long bitcoinInventedDate = 1230786000000L; + Date date = new Date(); + Date dateBefore = new Date(bitcoinInventedDate); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String today = sdf.format(date); + String dateBeforeString = sdf.format(dateBefore); + invoices = this.bitpay.getInvoices(dateBeforeString, today); + for (Invoice invoice : invoices) { + if (invoice.getStatus().equalsIgnoreCase("complete")) { + refundInvoiceId = invoice.getId(); + break; + } + } + } catch (BitPayException e) { + e.printStackTrace(); + } assertNotNull(refundInvoiceId); String bitcoinAddress = "2MvBKCRCtFBM4G7vN2WNfPh3vTjMAM8kfKb"; //change this to whatever address you want to refund to diff --git a/src/test/java/test/BitPayTest2.java b/src/test/java/test/BitPayTest2.java index 333a5b8..5abdeda 100644 --- a/src/test/java/test/BitPayTest2.java +++ b/src/test/java/test/BitPayTest2.java @@ -11,8 +11,6 @@ import java.net.UnknownHostException; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotNull; - public class BitPayTest2 { private BitPay bitpay;