diff --git a/src/main/java/com/bitpay/sdk/Client.java b/src/main/java/com/bitpay/sdk/Client.java index 68438ece..e61924d6 100644 --- a/src/main/java/com/bitpay/sdk/Client.java +++ b/src/main/java/com/bitpay/sdk/Client.java @@ -83,6 +83,17 @@ public Client(PosToken token) throws BitPayGenericException { this(token, Environment.PROD); } + /** + * Constructor for POS facade. + * + * @param token POS token + * @param platformInfo Platform info + * @throws BitPayGenericException BitPayGenericException class + */ + public Client(PosToken token, String platformInfo) throws BitPayGenericException { + this(token, Environment.PROD, platformInfo); + } + /** * Constructor for POS facade. * @@ -109,6 +120,35 @@ public Client( this.guidGenerator = new GuidGenerator(); } + /** + * Constructor for POS facade. + * + * @param token POS token + * @param environment Environment + * @param platformInfo Platform info + * @throws BitPayGenericException BitPayGenericException class + */ + public Client( + PosToken token, + Environment environment, + String platformInfo + ) throws BitPayGenericException { + if (Objects.isNull(token) || Objects.isNull(environment)) { + BitPayExceptionProvider.throwMissingParameterException(); + } + + this.tokenContainer = new TokenContainer(); + this.tokenContainer.addPos(token.value()); + this.bitPayClient = new BitPayClient( + getHttpClient(null, null), + new HttpRequestFactory(), + getBaseUrl(environment), + null, + platformInfo + ); + this.guidGenerator = new GuidGenerator(); + } + /** * Constructor for use if the keys and SIN are managed by this library. * @@ -138,6 +178,38 @@ public Client( this.guidGenerator = new GuidGenerator(); } + /** + * Constructor for use if the keys and SIN are managed by this library. + * + * @param environment Target environment. Options: Env.Test / Env.Prod + * @param privateKey The full path to the securely located private key or the HEX key value. + * @param tokenContainer Object containing the available tokens. + * @param proxyDetails HttpHost Optional Proxy setting (set to NULL to ignore) + * @param proxyCredentials CredentialsProvider Optional Proxy Basic Auth Credentials (set to NULL to ignore) + * @param platformInfo Platform Info + * @throws BitPayGenericException BitPayGenericException class + */ + public Client( + Environment environment, + PrivateKey privateKey, + TokenContainer tokenContainer, + HttpHost proxyDetails, + CredentialsProvider proxyCredentials, + String platformInfo + ) throws BitPayGenericException { + ECKey ecKey = getEcKey(privateKey); + this.tokenContainer = tokenContainer; + this.deriveIdentity(ecKey); + this.bitPayClient = new BitPayClient( + getHttpClient(proxyDetails, proxyCredentials), + new HttpRequestFactory(), + getBaseUrl(environment), + ecKey, + platformInfo + ); + this.guidGenerator = new GuidGenerator(); + } + /** * Constructor for use if the keys and SIN are managed by this library. * @@ -168,6 +240,40 @@ public Client( this.guidGenerator = new GuidGenerator(); } + + /** + * Constructor for use if the keys and SIN are managed by this library. + * + * @param configFilePath The path to the configuration file. + * @param proxy HttpHost Optional Proxy setting (set to NULL to ignore) + * @param proxyCredentials CredentialsProvider Optional Proxy Basic Auth Credentials (set to NULL to ignore) + * @param platformInfo Platform Info + * @throws BitPayGenericException BitPayGenericException class + */ + public Client( + ConfigFilePath configFilePath, + HttpHost proxy, + CredentialsProvider proxyCredentials, + String platformInfo + ) throws BitPayGenericException { + Config config = this.buildConfigFromFile(configFilePath); + this.tokenContainer = new TokenContainer(config); + ECKey ecKey = this.getEcKey(config); + if (Objects.isNull(ecKey)) { + BitPayExceptionProvider.throwValidationException("Missing ECKey"); + } + + this.deriveIdentity(ecKey); + this.bitPayClient = new BitPayClient( + getHttpClient(proxy, proxyCredentials), + new HttpRequestFactory(), + getBaseUrl(config.getEnvironment()), + ecKey, + platformInfo + ); + this.guidGenerator = new GuidGenerator(); + } + /** * Constructor for all injected classes. * @@ -199,6 +305,18 @@ public static Client createPosClient(PosToken token) throws BitPayGenericExcepti return new Client(token); } + /** + * Create pos (light) client. + * + * @param token the token + * @param platformInfo the platform info + * @return the client + * @throws BitPayGenericException BitPayGenericException class + */ + public static Client createPosClient(PosToken token, String platformInfo) throws BitPayGenericException { + return new Client(token, platformInfo); + } + /** * Create pos (light) client. * @@ -214,6 +332,23 @@ public static Client createPosClient( return new Client(token, environment); } + /** + * Create pos (light) client. + * + * @param token the token + * @param environment environment + * @param platformInfo the platform info + * @return the client + * @throws BitPayGenericException BitPayGenericException class + */ + public static Client createPosClient( + PosToken token, + Environment environment, + String platformInfo + ) throws BitPayGenericException { + return new Client(token, environment, platformInfo); + } + /** * Create standard client. * @@ -233,6 +368,27 @@ public static Client createClientByPrivateKey( return new Client(env, privateKey, tokenContainer, null, null); } + /** + * Create standard client. + * + * @param privateKey the private key + * @param tokenContainer the token container + * @param environment environment + * @param platformInfo the platform info + * @return Client Client + * @throws BitPayGenericException BitPayGenericException class + */ + public static Client createClientByPrivateKey( + PrivateKey privateKey, + TokenContainer tokenContainer, + Environment environment, + String platformInfo + ) throws BitPayGenericException { + Environment env = Objects.isNull(environment) ? Environment.PROD : environment; + + return new Client(env, privateKey, tokenContainer, null, null, platformInfo); + } + /** * Create standard client. * @@ -244,6 +400,21 @@ public static Client createClientByConfigFilePath(ConfigFilePath configFilePath) return new Client(configFilePath, null, null); } + /** + * Create standard client. + * + * @param configFilePath the config file path + * @param platformInfo the platform info + * @return the client + * @throws BitPayGenericException BitPayGenericException class + */ + public static Client createClientByConfigFilePath( + ConfigFilePath configFilePath, + String platformInfo + ) throws BitPayGenericException { + return new Client(configFilePath, null, null, platformInfo); + } + /** * Authorize this client with the server using the specified pairing code (Server Initiated Pairing). diff --git a/src/main/java/com/bitpay/sdk/client/BitPayClient.java b/src/main/java/com/bitpay/sdk/client/BitPayClient.java index 2b076db3..92a8a5f0 100644 --- a/src/main/java/com/bitpay/sdk/client/BitPayClient.java +++ b/src/main/java/com/bitpay/sdk/client/BitPayClient.java @@ -36,6 +36,7 @@ public class BitPayClient { private final HttpRequestFactory httpRequestFactory; private final String baseUrl; private final ECKey ecKey; + private String platformInfo = null; /** * Instantiates a new Bit pay client. @@ -57,6 +58,29 @@ public BitPayClient( this.ecKey = ecKey; } + /** + * Instantiates a new Bit pay client. + * + * @param httpClient the http client + * @param httpRequestFactory the http request factory + * @param baseUrl the base url + * @param ecKey the ECKey + * @param platformInfo the Platform Info + */ + public BitPayClient( + final HttpClient httpClient, + final HttpRequestFactory httpRequestFactory, + final String baseUrl, + final ECKey ecKey, + final String platformInfo + ) { + this.httpClient = httpClient; + this.baseUrl = baseUrl; + this.httpRequestFactory = httpRequestFactory; + this.ecKey = ecKey; + this.platformInfo = platformInfo; + } + /** * Send GET request. * @@ -241,12 +265,9 @@ public HttpResponse update( httpPut.setEntity(new ByteArrayEntity(json.getBytes(StandardCharsets.UTF_8))); - this.addSignatureRequiredHeaders(httpPut, endpoint + json); - httpPut.addHeader("x-accept-version", Config.BITPAY_API_VERSION); - httpPut.addHeader("X-BitPay-Plugin-Info", Config.BITPAY_PLUGIN_INFO); + this.addDefaultHeaders(httpPut); httpPut.addHeader("Content-Type", "application/json"); - httpPut.addHeader("x-bitpay-api-frame", Config.BITPAY_API_FRAME); - httpPut.addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); + this.addSignatureRequiredHeaders(httpPut, endpoint + json); LoggerProvider.getLogger().logRequest(HttpPut.METHOD_NAME, endpoint, httpPut.toString()); @@ -266,6 +287,9 @@ private void addDefaultHeaders(AbstractHttpMessage httpMessage) { httpMessage.addHeader("x-bitpay-api-frame", Config.BITPAY_API_FRAME); httpMessage.addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); httpMessage.addHeader("X-BitPay-Plugin-Info", Config.BITPAY_PLUGIN_INFO); + if (this.platformInfo != null && !this.platformInfo.isEmpty()) { + httpMessage.addHeader("x-bitPay-platform-info", this.platformInfo); + } } private void addSignatureRequiredHeaders(AbstractHttpMessage httpMessage, String uri) diff --git a/src/test/java/com/bitpay/sdk/ClientTest.java b/src/test/java/com/bitpay/sdk/ClientTest.java index 715ba8b2..f08ed65f 100644 --- a/src/test/java/com/bitpay/sdk/ClientTest.java +++ b/src/test/java/com/bitpay/sdk/ClientTest.java @@ -93,6 +93,18 @@ public void it_should_provide_pos_client() throws BitPayGenericException { Assertions.assertEquals(posToken, bitpay.getAccessToken(Facade.POS)); } + @Test + public void it_should_provide_pos_client_with_platform_info_header() throws BitPayGenericException { + // given + String posToken = "posToken"; + + // when + Client bitpay = Client.createPosClient(new PosToken(posToken), "MyPlatform_v1.0.0"); + + // then + Assertions.assertEquals(posToken, bitpay.getAccessToken(Facade.POS)); + } + @Test public void it_should_provide_client_by_key() throws BitPayGenericException { // given @@ -109,6 +121,22 @@ public void it_should_provide_client_by_key() throws BitPayGenericException { Assertions.assertEquals(merchantToken, bitpay.getAccessToken(Facade.MERCHANT)); } + @Test + public void it_should_provide_client_by_key_with_platform_info_header() throws BitPayGenericException { + // given + String privateKey = + "3082013102010104208ae30afbc7e93cb10cb983f70863e546b53f0b2c6158b1a71b576fd09790cff3a081e33081e0020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f3044042000000000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000000704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a124032200035d6a7e38d7c08b8a626e2390d0360a72a58bd1c5e1348e0eb810d4bbab3d3adf"; + String merchantToken = "merchantToken"; + TokenContainer tokens = new TokenContainer(); + tokens.addMerchant(merchantToken); + + // when + Client bitpay = Client.createClientByPrivateKey(new PrivateKey(privateKey), tokens, Environment.TEST, "MyPlatform_v1.0.0"); + + // then + Assertions.assertEquals(merchantToken, bitpay.getAccessToken(Facade.MERCHANT)); + } + @Test public void it_should_provide_client_by_config() throws BitPayGenericException { // given @@ -122,6 +150,19 @@ public void it_should_provide_client_by_config() throws BitPayGenericException { Assertions.assertEquals("payoutToken", bitpay.getAccessToken(Facade.PAYOUT)); } + @Test + public void it_should_provide_client_by_config_with_platform_info_header() throws BitPayGenericException { + // given + String path = System.getProperty("user.dir") + "/src/test/java/com/bitpay/sdk/BitPay.config.json"; + + // when + Client bitpay = Client.createClientByConfigFilePath(new ConfigFilePath(path), "MyPlatform_v1.0.0"); + + // then + Assertions.assertEquals("merchantToken", bitpay.getAccessToken(Facade.MERCHANT)); + Assertions.assertEquals("payoutToken", bitpay.getAccessToken(Facade.PAYOUT)); + } + @Test public void it_should_throws_BitPayApiException_for_invalid_privateKey() { BitPayGenericException exception = Assertions.assertThrows(BitPayGenericException.class, () -> { diff --git a/src/test/java/com/bitpay/sdk/client/BitPayClientTest.java b/src/test/java/com/bitpay/sdk/client/BitPayClientTest.java index 1499ecd4..110286e6 100644 --- a/src/test/java/com/bitpay/sdk/client/BitPayClientTest.java +++ b/src/test/java/com/bitpay/sdk/client/BitPayClientTest.java @@ -107,6 +107,43 @@ public void it_should_prepare_get_request_with_parameters() .addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); } + @Test + public void it_should_prepare_get_request_with_parameters_and_platform_info_header() + throws IOException, URISyntaxException { + // given + final String testUrl = "/test"; + final HttpGet httpGet = Mockito.mock(HttpGet.class); + final List parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("key", "value")); + + Mockito.when(this.httpRequestFactory.createHttpGet(BASE_URL + testUrl)).thenReturn(httpGet); + + String platformInfo = "MyPlatform_v1.0.0"; + BitPayClient testedClass = this.getTestedClass(platformInfo); + + // when + try { + testedClass.get(testUrl, parameters, true); + } catch (Exception e) { + // missing response + } + + // then + Mockito.verify(httpClient, Mockito.times(1)).execute(httpGet); + Mockito.verify(httpGet, Mockito.times(1)).setURI(new URI("http://localhost/test?key=value")); + Mockito.verify(httpGet, Mockito.times(1)) + .addHeader(ArgumentMatchers.eq("x-signature"), ArgumentMatchers.anyString()); + Mockito.verify(httpGet, Mockito.times(1)) + .addHeader(ArgumentMatchers.eq("x-identity"), ArgumentMatchers.anyString()); + Mockito.verify(httpGet, Mockito.times(1)).addHeader("X-BitPay-Plugin-Info", Config.BITPAY_PLUGIN_INFO); + Mockito.verify(httpGet, Mockito.times(1)).addHeader("x-accept-version", Config.BITPAY_API_VERSION); + Mockito.verify(httpGet, Mockito.times(1)).addHeader("x-bitpay-api-frame", Config.BITPAY_API_FRAME); + Mockito.verify(httpGet, Mockito.times(1)) + .addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); + Mockito.verify(httpGet, Mockito.times(1)) + .addHeader("x-bitPay-platform-info", platformInfo); + } + @Test public void it_should_throws_bitpay_api_exception_when_something_is_wrong_for_get_requests() { BitPayApiException exception1 = Assertions.assertThrows( @@ -306,6 +343,42 @@ public void it_should_prepare_delete_request() throws IOException, URISyntaxExce .addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); } + @Test + public void it_should_prepare_delete_request_with_platform_info() throws IOException, URISyntaxException { + // given + final String testUrl = "/test"; + final HttpDelete httpDelete = Mockito.mock(HttpDelete.class); + final List parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("key", "value")); + + Mockito.when(this.httpRequestFactory.createHttpDelete(BASE_URL + testUrl)).thenReturn(httpDelete); + + String platformInfo = "MyPlatform_v1.0.0"; + BitPayClient testedClass = this.getTestedClass(platformInfo); + + // when + try { + testedClass.delete(testUrl, parameters); + } catch (Exception e) { + // missing response + } + + // then + Mockito.verify(httpClient, Mockito.times(1)).execute(httpDelete); + Mockito.verify(httpDelete, Mockito.times(1)).setURI(new URI("http://localhost/test?key=value")); + Mockito.verify(httpDelete, Mockito.times(1)) + .addHeader(ArgumentMatchers.eq("x-signature"), ArgumentMatchers.anyString()); + Mockito.verify(httpDelete, Mockito.times(1)) + .addHeader(ArgumentMatchers.eq("x-identity"), ArgumentMatchers.anyString()); + Mockito.verify(httpDelete, Mockito.times(1)).addHeader("X-BitPay-Plugin-Info", Config.BITPAY_PLUGIN_INFO); + Mockito.verify(httpDelete, Mockito.times(1)).addHeader("x-accept-version", Config.BITPAY_API_VERSION); + Mockito.verify(httpDelete, Mockito.times(1)).addHeader("x-bitpay-api-frame", Config.BITPAY_API_FRAME); + Mockito.verify(httpDelete, Mockito.times(1)) + .addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); + Mockito.verify(httpDelete, Mockito.times(1)) + .addHeader("x-bitPay-platform-info", platformInfo); + } + @Test public void it_should_prepare_post_request() throws IOException { // given @@ -338,6 +411,41 @@ public void it_should_prepare_post_request() throws IOException { .addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); } + @Test + public void it_should_prepare_post_request_with_platform_info_header() throws IOException { + // given + final String testUrl = "/test"; + final String json = "{\"key\": \"value\"}"; + final HttpPost httpPost = Mockito.mock(HttpPost.class); + + Mockito.when(this.httpRequestFactory.createHttpPost(BASE_URL + testUrl)).thenReturn(httpPost); + + String platformInfo = "MyPlatform_v1.0.0"; + BitPayClient testedClass = this.getTestedClass(platformInfo); + + // when + try { + testedClass.post(testUrl, json, true); + } catch (Exception e) { + // missing response + } + + // then + Mockito.verify(httpClient, Mockito.times(1)).execute(httpPost); + Mockito.verify(httpPost, Mockito.times(1)).setEntity(ArgumentMatchers.any(ByteArrayEntity.class)); + Mockito.verify(httpPost, Mockito.times(1)) + .addHeader(ArgumentMatchers.eq("x-signature"), ArgumentMatchers.anyString()); + Mockito.verify(httpPost, Mockito.times(1)) + .addHeader(ArgumentMatchers.eq("x-identity"), ArgumentMatchers.anyString()); + Mockito.verify(httpPost, Mockito.times(1)).addHeader("X-BitPay-Plugin-Info", Config.BITPAY_PLUGIN_INFO); + Mockito.verify(httpPost, Mockito.times(1)).addHeader("x-accept-version", Config.BITPAY_API_VERSION); + Mockito.verify(httpPost, Mockito.times(1)).addHeader("x-bitpay-api-frame", Config.BITPAY_API_FRAME); + Mockito.verify(httpPost, Mockito.times(1)) + .addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); + Mockito.verify(httpPost, Mockito.times(1)) + .addHeader("x-bitPay-platform-info", platformInfo); + } + @Test public void it_should_prepare_put_request() throws BitPayGenericException, BitPayApiException, IOException { // given @@ -370,9 +478,48 @@ public void it_should_prepare_put_request() throws BitPayGenericException, BitPa .addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); } + @Test + public void it_should_prepare_put_request_with_platform_info_header() throws BitPayGenericException, BitPayApiException, IOException { + // given + final String testUrl = "/test"; + final String json = "{\"key\": \"value\"}"; + final HttpPut httpPut = Mockito.mock(HttpPut.class); + + Mockito.when(this.httpRequestFactory.createHttpPut(BASE_URL + testUrl)).thenReturn(httpPut); + + String platformInfo = "MyPlatform_v1.0.0"; + BitPayClient testedClass = this.getTestedClass(platformInfo); + + // when + try { + testedClass.update(testUrl, json); + } catch (Exception e) { + // missing response + } + + // then + Mockito.verify(httpClient, Mockito.times(1)).execute(httpPut); + Mockito.verify(httpPut, Mockito.times(1)).setEntity(ArgumentMatchers.any(ByteArrayEntity.class)); + Mockito.verify(httpPut, Mockito.times(1)) + .addHeader(ArgumentMatchers.eq("x-signature"), ArgumentMatchers.anyString()); + Mockito.verify(httpPut, Mockito.times(1)) + .addHeader(ArgumentMatchers.eq("x-identity"), ArgumentMatchers.anyString()); + Mockito.verify(httpPut, Mockito.times(1)).addHeader("X-BitPay-Plugin-Info", Config.BITPAY_PLUGIN_INFO); + Mockito.verify(httpPut, Mockito.times(1)).addHeader("x-accept-version", Config.BITPAY_API_VERSION); + Mockito.verify(httpPut, Mockito.times(1)).addHeader("x-bitpay-api-frame", Config.BITPAY_API_FRAME); + Mockito.verify(httpPut, Mockito.times(1)) + .addHeader("x-bitpay-api-frame-version", Config.BITPAY_API_FRAME_VERSION); + Mockito.verify(httpPut, Mockito.times(1)) + .addHeader("x-bitPay-platform-info", platformInfo); + } + private BitPayClient getTestedClass() { return new BitPayClient(this.httpClient, this.httpRequestFactory, BASE_URL, new ECKey()); } + + private BitPayClient getTestedClass(String platformInfo) { + return new BitPayClient(this.httpClient, this.httpRequestFactory, BASE_URL, new ECKey(), platformInfo); + } }