/* * This file is part of Bitsquare. * * Bitsquare is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * Bitsquare is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public * License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>. */ package io.bitsquare.payment; import io.bitsquare.app.Version; import io.bitsquare.common.persistance.Persistable; import org.bitcoinj.core.Coin; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; // Don't use Enum as it breaks serialisation when changing entries and we want to stay flexible here public final class PaymentMethod implements Persistable, Comparable { // That object is saved to disc. We need to take care of changes to not break deserialization. private static final long serialVersionUID = Version.LOCAL_DB_VERSION; protected final Logger log = LoggerFactory.getLogger(this.getClass()); // time in blocks (average 10 min for one block confirmation private static final long HOUR = 3600_000; private static final long DAY = HOUR * 24; public static final String OK_PAY_ID = "OK_PAY"; public static final String PERFECT_MONEY_ID = "PERFECT_MONEY"; public static final String SEPA_ID = "SEPA"; public static final String FASTER_PAYMENTS_ID = "FASTER_PAYMENTS"; public static final String NATIONAL_BANK_ID = "NATIONAL_BANK"; public static final String SAME_BANK_ID = "SAME_BANK"; public static final String SPECIFIC_BANKS_ID = "SPECIFIC_BANKS"; public static final String SWISH_ID = "SWISH"; public static final String ALI_PAY_ID = "ALI_PAY"; public static final String CLEAR_X_CHANGE_ID = "CLEAR_X_CHANGE"; public static final String CHASE_QUICK_PAY_ID = "CHASE_QUICK_PAY"; public static final String INTERAC_E_TRANSFER_ID = "INTERAC_E_TRANSFER"; public static final String US_POSTAL_MONEY_ORDER_ID = "US_POSTAL_MONEY_ORDER"; public static final String CASH_DEPOSIT_ID = "CASH_DEPOSIT"; public static final String BLOCK_CHAINS_ID = "BLOCK_CHAINS"; public static PaymentMethod OK_PAY; public static PaymentMethod PERFECT_MONEY; public static PaymentMethod SEPA; public static PaymentMethod FASTER_PAYMENTS; public static PaymentMethod NATIONAL_BANK; public static PaymentMethod SAME_BANK; public static PaymentMethod SPECIFIC_BANKS; public static PaymentMethod SWISH; public static PaymentMethod ALI_PAY; public static PaymentMethod CLEAR_X_CHANGE; public static PaymentMethod CHASE_QUICK_PAY; public static PaymentMethod INTERAC_E_TRANSFER; public static PaymentMethod US_POSTAL_MONEY_ORDER; public static PaymentMethod CASH_DEPOSIT; public static PaymentMethod BLOCK_CHAINS; public static final List<PaymentMethod> ALL_VALUES = new ArrayList<>(Arrays.asList( // EUR SEPA = new PaymentMethod(SEPA_ID, 0, 8 * DAY, Coin.parseCoin("1")), // sepa takes 1-3 business days. We use 8 days to include safety for holidays // Global NATIONAL_BANK = new PaymentMethod(NATIONAL_BANK_ID, 0, 4 * DAY, Coin.parseCoin("1")), SAME_BANK = new PaymentMethod(SAME_BANK_ID, 0, 2 * DAY, Coin.parseCoin("1")), SPECIFIC_BANKS = new PaymentMethod(SPECIFIC_BANKS_ID, 0, 4 * DAY, Coin.parseCoin("1")), CASH_DEPOSIT = new PaymentMethod(CASH_DEPOSIT_ID, 0, 4 * DAY, Coin.parseCoin("1")), // Trans national OK_PAY = new PaymentMethod(OK_PAY_ID, 0, DAY, Coin.parseCoin("2")), PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, 0, DAY, Coin.parseCoin("1")), // UK FASTER_PAYMENTS = new PaymentMethod(FASTER_PAYMENTS_ID, 0, DAY, Coin.parseCoin("1")), // Canada INTERAC_E_TRANSFER = new PaymentMethod(INTERAC_E_TRANSFER_ID, 0, DAY, Coin.parseCoin("1")), // US CLEAR_X_CHANGE = new PaymentMethod(CLEAR_X_CHANGE_ID, 0, 4 * DAY, Coin.parseCoin("1")), CHASE_QUICK_PAY = new PaymentMethod(CHASE_QUICK_PAY_ID, 0, DAY, Coin.parseCoin("1")), US_POSTAL_MONEY_ORDER = new PaymentMethod(US_POSTAL_MONEY_ORDER_ID, 0, 8 * DAY, Coin.parseCoin("1")), // Sweden SWISH = new PaymentMethod(SWISH_ID, 0, DAY, Coin.parseCoin("2")), // China ALI_PAY = new PaymentMethod(ALI_PAY_ID, 0, DAY, Coin.parseCoin("2")), // Altcoins BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, 0, DAY, Coin.parseCoin("3")) )); private final String id; private long lockTime; private long maxTradePeriod; private Coin maxTradeLimit; /** * @param id * @param lockTime lock time when seller release BTC until the payout tx gets valid (bitcoin tx lockTime). Serves as protection * against charge back risk. If Bank do the charge back quickly the Arbitrator and the seller can push another * double spend tx to invalidate the time locked payout tx. For the moment we set all to 0 but will have it in * place when needed. * @param maxTradePeriod The min. period a trader need to wait until he gets displayed the contact form for opening a dispute. * @param maxTradeLimit The max. allowed trade amount in Bitcoin for that payment method (depending on charge back risk) */ public PaymentMethod(String id, long lockTime, long maxTradePeriod, Coin maxTradeLimit) { this.id = id; this.lockTime = lockTime; this.maxTradePeriod = maxTradePeriod; this.maxTradeLimit = maxTradeLimit; } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { try { in.defaultReadObject(); // In case we update those values we want that the persisted accounts get updated as well PaymentMethod paymentMethod = PaymentMethod.getPaymentMethodById(id); this.lockTime = paymentMethod.getLockTime(); this.maxTradePeriod = paymentMethod.getMaxTradePeriod(); this.maxTradeLimit = paymentMethod.getMaxTradeLimit(); } catch (Throwable t) { log.warn("Cannot be deserialized." + t.getMessage()); } } public static PaymentMethod getPaymentMethodById(String name) { Optional<PaymentMethod> paymentMethodOptional = ALL_VALUES.stream().filter(e -> e.getId().equals(name)).findFirst(); if (paymentMethodOptional.isPresent()) return paymentMethodOptional.get(); else return new PaymentMethod("N/A", 1, DAY, Coin.parseCoin("0")); } public String getId() { return id; } public long getMaxTradePeriod() { return maxTradePeriod; } public long getLockTime() { return lockTime; } public Coin getMaxTradeLimit() { return maxTradeLimit; } @Override public int compareTo(@NotNull Object other) { if (id != null) return this.id.compareTo(((PaymentMethod) other).id); else return 0; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PaymentMethod)) return false; PaymentMethod that = (PaymentMethod) o; if (lockTime != that.lockTime) return false; if (maxTradePeriod != that.maxTradePeriod) return false; if (id != null ? !id.equals(that.id) : that.id != null) return false; return !(maxTradeLimit != null ? !maxTradeLimit.equals(that.maxTradeLimit) : that.maxTradeLimit != null); } @Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (int) (lockTime ^ (lockTime >>> 32)); result = 31 * result + (int) (maxTradePeriod ^ (maxTradePeriod >>> 32)); result = 31 * result + (maxTradeLimit != null ? maxTradeLimit.hashCode() : 0); return result; } @Override public String toString() { return "PaymentMethod{" + "id='" + id + '\'' + ", lockTime=" + lockTime + ", maxTradePeriod=" + maxTradePeriod + ", maxTradeLimitInBitcoin=" + maxTradeLimit + '}'; } }