Fix the CoinbaseClient to use their updated auth.
Closes #43 // FREEBIE
This commit is contained in:
parent
b15d227696
commit
20b6aebb29
@ -18,3 +18,4 @@ github:
|
||||
|
||||
coinbase:
|
||||
apiKey: # Your Coinbase API key.
|
||||
apiSecret: # Your Coinbase API secret.
|
||||
|
||||
@ -21,6 +21,7 @@ import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||
import org.whispersystems.bithub.auth.GithubWebhookAuthenticator;
|
||||
import org.whispersystems.bithub.client.CoinbaseClient;
|
||||
import org.whispersystems.bithub.client.GithubClient;
|
||||
import org.whispersystems.bithub.config.CoinbaseConfiguration;
|
||||
import org.whispersystems.bithub.config.RepositoryConfiguration;
|
||||
import org.whispersystems.bithub.controllers.DashboardController;
|
||||
import org.whispersystems.bithub.controllers.GithubController;
|
||||
@ -64,9 +65,11 @@ public class BithubService extends Application<BithubServerConfiguration> {
|
||||
BigDecimal payoutRate = config.getBithubConfiguration().getPayoutRate();
|
||||
String organizationName = config.getOrganizationConfiguration().getName();
|
||||
String donationUrl = config.getOrganizationConfiguration().getDonationUrl().toExternalForm();
|
||||
String coinbaseApiKey = config.getCoinbaseConfiguration().getApiKey();
|
||||
String coinbaseApiSecret = config.getCoinbaseConfiguration().getApiSecret();
|
||||
|
||||
GithubClient githubClient = new GithubClient(githubUser, githubToken);
|
||||
CoinbaseClient coinbaseClient = new CoinbaseClient(config.getCoinbaseConfiguration().getApiKey());
|
||||
CoinbaseClient coinbaseClient = new CoinbaseClient(coinbaseApiKey, coinbaseApiSecret);
|
||||
CacheManager cacheManager = new CacheManager(coinbaseClient, githubClient, githubRepositories, payoutRate);
|
||||
|
||||
environment.servlets().addFilter("CORS", CrossOriginFilter.class)
|
||||
|
||||
@ -24,18 +24,23 @@ import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.api.client.config.ClientConfig;
|
||||
import com.sun.jersey.api.client.config.DefaultClientConfig;
|
||||
import com.sun.jersey.api.json.JSONConfiguration;
|
||||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.whispersystems.bithub.entities.Author;
|
||||
import org.whispersystems.bithub.entities.BalanceResponse;
|
||||
import org.whispersystems.bithub.entities.BitcoinTransaction;
|
||||
import org.whispersystems.bithub.entities.BitcoinTransactionResponse;
|
||||
import org.whispersystems.bithub.entities.ExchangeRate;
|
||||
import org.whispersystems.bithub.entities.CoinbseRecentTransactionsResponse;
|
||||
import org.whispersystems.bithub.entities.CoinbaseTransaction;
|
||||
import org.whispersystems.bithub.entities.CoinbseRecentTransactionsResponse;
|
||||
import org.whispersystems.bithub.entities.ExchangeRate;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -45,26 +50,30 @@ import java.util.List;
|
||||
*/
|
||||
public class CoinbaseClient {
|
||||
|
||||
private static final String COINBASE_URL = "https://coinbase.com/";
|
||||
private static final String COINBASE_URL = "https://coinbase.com";
|
||||
private static final String BALANCE_PATH = "/api/v1/account/balance";
|
||||
private static final String PAYMENT_PATH = "/api/v1/transactions/send_money";
|
||||
private static final String EXCHANGE_PATH = "/api/v1/currencies/exchange_rates";
|
||||
private static final String RECENT_TRANSACTIONS_PATH = "/api/v1/transactions";
|
||||
|
||||
private final String apiKey;
|
||||
private final String apiSecret;
|
||||
private final Client client;
|
||||
|
||||
public CoinbaseClient(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
this.client = Client.create(getClientConfig());
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public CoinbaseClient(String apiKey, String apiSecret) {
|
||||
this.apiKey = apiKey;
|
||||
this.apiSecret = apiSecret;
|
||||
this.client = Client.create(getClientConfig());
|
||||
}
|
||||
|
||||
public List<CoinbaseTransaction> getRecentTransactions() throws IOException {
|
||||
public List<CoinbaseTransaction> getRecentTransactions()
|
||||
throws IOException, TransferFailedException
|
||||
{
|
||||
try {
|
||||
return client.resource(COINBASE_URL)
|
||||
.path(RECENT_TRANSACTIONS_PATH)
|
||||
.queryParam("api_key", apiKey)
|
||||
.get(CoinbseRecentTransactionsResponse.class).getTransactions();
|
||||
return getAuthenticatedWebResource(RECENT_TRANSACTIONS_PATH, null).get(CoinbseRecentTransactionsResponse.class)
|
||||
.getTransactions();
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
@ -89,23 +98,19 @@ public class CoinbaseClient {
|
||||
throws TransferFailedException
|
||||
{
|
||||
try {
|
||||
WebResource resource = client.resource(COINBASE_URL)
|
||||
.path(PAYMENT_PATH)
|
||||
.queryParam("api_key", apiKey);
|
||||
|
||||
String note = "Commit payment:\n__" + author.getUsername() + "__ " + url;
|
||||
|
||||
BitcoinTransaction transaction = new BitcoinTransaction(author.getEmail(),
|
||||
amount.toPlainString(),
|
||||
note);
|
||||
|
||||
boolean success = resource.type(MediaType.APPLICATION_JSON_TYPE)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.entity(transaction)
|
||||
.post(BitcoinTransactionResponse.class)
|
||||
.isSuccess();
|
||||
WebResource.Builder resource = getAuthenticatedWebResource(PAYMENT_PATH, transaction);
|
||||
|
||||
if (!success) {
|
||||
BitcoinTransactionResponse response = resource.type(MediaType.APPLICATION_JSON_TYPE)
|
||||
.entity(transaction)
|
||||
.post(BitcoinTransactionResponse.class);
|
||||
|
||||
if (!response.isSuccess()) {
|
||||
throw new TransferFailedException();
|
||||
}
|
||||
|
||||
@ -114,16 +119,11 @@ public class CoinbaseClient {
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal getAccountBalance() throws IOException {
|
||||
public BigDecimal getAccountBalance() throws IOException, TransferFailedException {
|
||||
try {
|
||||
WebResource resource = client.resource(COINBASE_URL)
|
||||
.path(BALANCE_PATH)
|
||||
.queryParam("api_key", apiKey);
|
||||
|
||||
String amount = resource.accept(MediaType.APPLICATION_JSON)
|
||||
.get(BalanceResponse.class)
|
||||
WebResource.Builder resource = getAuthenticatedWebResource(BALANCE_PATH, null);
|
||||
String amount = resource.get(BalanceResponse.class)
|
||||
.getAmount();
|
||||
|
||||
if (amount == null) {
|
||||
throw new IOException("Empty amount in response!");
|
||||
}
|
||||
@ -134,6 +134,27 @@ public class CoinbaseClient {
|
||||
}
|
||||
}
|
||||
|
||||
private WebResource.Builder getAuthenticatedWebResource(String path, Object body) throws TransferFailedException {
|
||||
try {
|
||||
String json = body == null ? "" : objectMapper.writeValueAsString(body);
|
||||
String nonce = String.valueOf(System.currentTimeMillis());
|
||||
String message = nonce + COINBASE_URL + path + json;
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(apiSecret.getBytes(), "HmacSHA256"));
|
||||
|
||||
String signature = new String(Hex.encodeHex(mac.doFinal(message.getBytes())));
|
||||
|
||||
return client.resource(COINBASE_URL)
|
||||
.path(path)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("ACCESS_SIGNATURE", signature)
|
||||
.header("ACCESS_NONCE", nonce)
|
||||
.header("ACCESS_KEY", apiKey);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | IOException e) {
|
||||
throw new TransferFailedException();
|
||||
}
|
||||
}
|
||||
|
||||
private ClientConfig getClientConfig() {
|
||||
ClientConfig config = new DefaultClientConfig();
|
||||
config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
|
||||
|
||||
@ -17,13 +17,13 @@
|
||||
|
||||
package org.whispersystems.bithub.client;
|
||||
|
||||
public class TransferFailedException extends Throwable {
|
||||
public class TransferFailedException extends Exception {
|
||||
|
||||
public TransferFailedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TransferFailedException(RuntimeException e) {
|
||||
public TransferFailedException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,15 @@ public class CoinbaseConfiguration {
|
||||
@NotEmpty
|
||||
private String apiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String apiSecret;
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public String getApiSecret() {
|
||||
return apiSecret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ public class GithubController {
|
||||
public void handleCommits(@Auth Authentication auth,
|
||||
@HeaderParam("X-Forwarded-For") String clientIp,
|
||||
@FormParam("payload") String eventString)
|
||||
throws IOException, UnauthorizedHookException
|
||||
throws IOException, UnauthorizedHookException, TransferFailedException
|
||||
{
|
||||
authenticate(clientIp);
|
||||
PushEvent event = getEventFromPayload(eventString);
|
||||
|
||||
@ -22,13 +22,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
public class BitcoinTransaction {
|
||||
|
||||
@JsonProperty
|
||||
private String to;
|
||||
public String to;
|
||||
|
||||
@JsonProperty
|
||||
private String amount;
|
||||
public String amount;
|
||||
|
||||
@JsonProperty
|
||||
private String notes;
|
||||
public String notes;
|
||||
|
||||
public BitcoinTransaction(String to, String amount, String notes) {
|
||||
this.to = to;
|
||||
|
||||
@ -19,12 +19,19 @@ package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class BitcoinTransactionResponse {
|
||||
|
||||
private boolean success;
|
||||
private List<String> errors;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setErrors(List<String> errors) {
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.bithub.client.CoinbaseClient;
|
||||
import org.whispersystems.bithub.client.GithubClient;
|
||||
import org.whispersystems.bithub.client.TransferFailedException;
|
||||
import org.whispersystems.bithub.config.RepositoryConfiguration;
|
||||
import org.whispersystems.bithub.entities.CoinbaseTransaction;
|
||||
import org.whispersystems.bithub.entities.Payment;
|
||||
@ -94,7 +95,7 @@ public class CacheManager implements Managed {
|
||||
cachedTransactions.set(transactions);
|
||||
cachedRepositories.set(repositories);
|
||||
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | TransferFailedException e) {
|
||||
logger.warn("Failed to update badge", e);
|
||||
}
|
||||
}
|
||||
@ -114,7 +115,7 @@ public class CacheManager implements Managed {
|
||||
}
|
||||
|
||||
private CurrentPayment createCurrentPaymentForBalance(CoinbaseClient coinbaseClient)
|
||||
throws IOException
|
||||
throws IOException, TransferFailedException
|
||||
{
|
||||
BigDecimal currentBalance = coinbaseClient.getAccountBalance();
|
||||
BigDecimal paymentBtc = currentBalance.multiply(payoutRate);
|
||||
@ -128,7 +129,7 @@ public class CacheManager implements Managed {
|
||||
}
|
||||
|
||||
private List<Transaction> createRecentTransactions(CoinbaseClient coinbaseClient)
|
||||
throws IOException
|
||||
throws IOException, TransferFailedException
|
||||
{
|
||||
List<CoinbaseTransaction> recentTransactions = coinbaseClient.getRecentTransactions();
|
||||
BigDecimal exchangeRate = coinbaseClient.getExchangeRate();
|
||||
|
||||
@ -77,7 +77,7 @@ public class GithubControllerTest {
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
public void setup() throws Exception, TransferFailedException {
|
||||
when(coinbaseClient.getAccountBalance()).thenReturn(BALANCE);
|
||||
when(coinbaseClient.getExchangeRate()).thenReturn(EXCHANGE_RATE);
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.bithub.client.CoinbaseClient;
|
||||
import org.whispersystems.bithub.client.GithubClient;
|
||||
import org.whispersystems.bithub.client.TransferFailedException;
|
||||
import org.whispersystems.bithub.config.RepositoryConfiguration;
|
||||
import org.whispersystems.bithub.controllers.StatusController;
|
||||
import org.whispersystems.bithub.entities.CoinbseRecentTransactionsResponse;
|
||||
@ -49,7 +50,7 @@ public class StatusControllerTest {
|
||||
resources = ResourceTestRule.builder()
|
||||
.addResource(new StatusController(coinbaseManager, null))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
} catch (Exception | TransferFailedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user