/* * This file is part of Bisq. * * Bisq 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. * * Bisq 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 Bisq. If not, see . */ package bisq.core.payment.payload; import bisq.core.app.BisqEnvironment; import bisq.core.locale.Res; import bisq.common.proto.persistable.PersistablePayload; import io.bisq.generated.protobuffer.PB; import org.bitcoinj.core.Coin; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; @EqualsAndHashCode(exclude = {"maxTradePeriod", "maxTradeLimit"}) @ToString @Slf4j public final class PaymentMethod implements PersistablePayload, Comparable { /////////////////////////////////////////////////////////////////////////////////////////// // Static /////////////////////////////////////////////////////////////////////////////////////////// // time in blocks (average 10 min for one block confirmation private static final long DAY = TimeUnit.HOURS.toMillis(24); public static final String OK_PAY_ID = "OK_PAY"; public static final String UPHOLD_ID = "UPHOLD"; @Deprecated public static final String CASH_APP_ID = "CASH_APP"; // Removed due too high chargeback risk public static final String MONEY_BEAM_ID = "MONEY_BEAM"; @Deprecated public static final String VENMO_ID = "VENMO"; // Removed due too high chargeback risk public static final String POPMONEY_ID = "POPMONEY"; public static final String REVOLUT_ID = "REVOLUT"; public static final String PERFECT_MONEY_ID = "PERFECT_MONEY"; public static final String SEPA_ID = "SEPA"; public static final String SEPA_INSTANT_ID = "SEPA_INSTANT"; 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 WECHAT_PAY_ID = "WECHAT_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 MONEY_GRAM_ID = "MONEY_GRAM"; public static final String WESTERN_UNION_ID = "WESTERN_UNION"; public static final String HAL_CASH_ID = "HAL_CASH"; public static final String F2F_ID = "F2F"; public static final String BLOCK_CHAINS_ID = "BLOCK_CHAINS"; public static PaymentMethod OK_PAY; public static PaymentMethod UPHOLD; @Deprecated public static PaymentMethod CASH_APP; // Removed due too high chargeback risk public static PaymentMethod MONEY_BEAM; @Deprecated public static PaymentMethod VENMO; // Removed due too high chargeback risk public static PaymentMethod POPMONEY; public static PaymentMethod REVOLUT; public static PaymentMethod PERFECT_MONEY; public static PaymentMethod SEPA; public static PaymentMethod SEPA_INSTANT; 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 WECHAT_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 MONEY_GRAM; public static PaymentMethod WESTERN_UNION; public static PaymentMethod F2F; public static PaymentMethod HAL_CASH; public static PaymentMethod BLOCK_CHAINS; private static List ALL_VALUES; public static void onAllServicesInitialized() { getAllValues(); } /////////////////////////////////////////////////////////////////////////////////////////// // Instance fields /////////////////////////////////////////////////////////////////////////////////////////// @Getter private final String id; @Getter private final long maxTradePeriod; private final long maxTradeLimit; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////////////////// /** * @param id 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 maxTradePeriod, @NotNull Coin maxTradeLimit) { this.id = id; this.maxTradePeriod = maxTradePeriod; this.maxTradeLimit = maxTradeLimit.value; } public static List getAllValues() { if (ALL_VALUES == null) { Coin maxTradeLimitHighRisk; Coin maxTradeLimitMidRisk; Coin maxTradeLimitLowRisk; Coin maxTradeLimitVeryLowRisk; switch (BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()) { case "BTC": // we want to avoid more then 4 decimal places (0.125 / 4 = 0.03125), so we use a bit higher value to get 0.04 for first month maxTradeLimitHighRisk = Coin.parseCoin("0.16"); maxTradeLimitMidRisk = Coin.parseCoin("0.25"); maxTradeLimitLowRisk = Coin.parseCoin("0.5"); maxTradeLimitVeryLowRisk = Coin.parseCoin("1"); break; case "LTC": maxTradeLimitHighRisk = Coin.parseCoin("12.5"); maxTradeLimitMidRisk = Coin.parseCoin("25"); maxTradeLimitLowRisk = Coin.parseCoin("50"); maxTradeLimitVeryLowRisk = Coin.parseCoin("100"); break; case "DASH": maxTradeLimitHighRisk = Coin.parseCoin("5"); maxTradeLimitMidRisk = Coin.parseCoin("10"); maxTradeLimitLowRisk = Coin.parseCoin("20"); maxTradeLimitVeryLowRisk = Coin.parseCoin("40"); break; default: log.error("Unsupported BaseCurrency. " + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()); throw new RuntimeException("Unsupported BaseCurrency. " + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()); } ALL_VALUES = new ArrayList<>(Arrays.asList( // EUR SEPA = new PaymentMethod(SEPA_ID, 6 * DAY, maxTradeLimitMidRisk), SEPA_INSTANT = new PaymentMethod(SEPA_INSTANT_ID, DAY, maxTradeLimitMidRisk), MONEY_BEAM = new PaymentMethod(MONEY_BEAM_ID, DAY, maxTradeLimitHighRisk), // UK FASTER_PAYMENTS = new PaymentMethod(FASTER_PAYMENTS_ID, DAY, maxTradeLimitMidRisk), // Sweden SWISH = new PaymentMethod(SWISH_ID, DAY, maxTradeLimitLowRisk), // US CLEAR_X_CHANGE = new PaymentMethod(CLEAR_X_CHANGE_ID, 4 * DAY, maxTradeLimitMidRisk), CASH_APP = new PaymentMethod(CASH_APP_ID, DAY, maxTradeLimitHighRisk), VENMO = new PaymentMethod(VENMO_ID, DAY, maxTradeLimitHighRisk), POPMONEY = new PaymentMethod(POPMONEY_ID, DAY, maxTradeLimitHighRisk), CHASE_QUICK_PAY = new PaymentMethod(CHASE_QUICK_PAY_ID, DAY, maxTradeLimitMidRisk), US_POSTAL_MONEY_ORDER = new PaymentMethod(US_POSTAL_MONEY_ORDER_ID, 8 * DAY, maxTradeLimitMidRisk), // Canada INTERAC_E_TRANSFER = new PaymentMethod(INTERAC_E_TRANSFER_ID, DAY, maxTradeLimitMidRisk), // Global CASH_DEPOSIT = new PaymentMethod(CASH_DEPOSIT_ID, 4 * DAY, maxTradeLimitMidRisk), MONEY_GRAM = new PaymentMethod(MONEY_GRAM_ID, 4 * DAY, maxTradeLimitMidRisk), WESTERN_UNION = new PaymentMethod(WESTERN_UNION_ID, 4 * DAY, maxTradeLimitMidRisk), NATIONAL_BANK = new PaymentMethod(NATIONAL_BANK_ID, 4 * DAY, maxTradeLimitMidRisk), SAME_BANK = new PaymentMethod(SAME_BANK_ID, 2 * DAY, maxTradeLimitMidRisk), SPECIFIC_BANKS = new PaymentMethod(SPECIFIC_BANKS_ID, 4 * DAY, maxTradeLimitMidRisk), HAL_CASH = new PaymentMethod(HAL_CASH_ID, DAY, maxTradeLimitLowRisk), F2F = new PaymentMethod(F2F_ID, 4 * DAY, maxTradeLimitLowRisk), // Trans national OK_PAY = new PaymentMethod(OK_PAY_ID, DAY, maxTradeLimitVeryLowRisk), UPHOLD = new PaymentMethod(UPHOLD_ID, DAY, maxTradeLimitHighRisk), REVOLUT = new PaymentMethod(REVOLUT_ID, DAY, maxTradeLimitHighRisk), PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, DAY, maxTradeLimitLowRisk), // China ALI_PAY = new PaymentMethod(ALI_PAY_ID, DAY, maxTradeLimitLowRisk), WECHAT_PAY = new PaymentMethod(WECHAT_PAY_ID, DAY, maxTradeLimitLowRisk), // Altcoins BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, DAY, maxTradeLimitVeryLowRisk) )); } ALL_VALUES.sort((o1, o2) -> { String id1 = o1.getId(); if (id1.equals(CLEAR_X_CHANGE_ID)) id1 = "ZELLE"; String id2 = o2.getId(); if (id2.equals(CLEAR_X_CHANGE_ID)) id2 = "ZELLE"; return id1.compareTo(id2); }); return ALL_VALUES; } /////////////////////////////////////////////////////////////////////////////////////////// // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// @Override public PB.PaymentMethod toProtoMessage() { return PB.PaymentMethod.newBuilder() .setId(id) .setMaxTradePeriod(maxTradePeriod) .setMaxTradeLimit(maxTradeLimit) .build(); } public static PaymentMethod fromProto(PB.PaymentMethod proto) { return new PaymentMethod(proto.getId(), proto.getMaxTradePeriod(), Coin.valueOf(proto.getMaxTradeLimit())); } /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// // Used for dummy entries in payment methods list (SHOW_ALL) public PaymentMethod(String id) { this(id, 0, Coin.ZERO); } public static PaymentMethod getPaymentMethodById(String id) { Optional paymentMethodOptional = getAllValues().stream().filter(e -> e.getId().equals(id)).findFirst(); return paymentMethodOptional.orElseGet(() -> new PaymentMethod(Res.get("shared.na"))); } // Hack for SF as the smallest unit is 1 SF ;-( and price is about 3 BTC! public Coin getMaxTradeLimitAsCoin(String currencyCode) { if (currencyCode.equals("SF") || currencyCode.equals("BSQ")) return Coin.parseCoin("4"); else return Coin.valueOf(maxTradeLimit); } @Override public int compareTo(@NotNull Object other) { if (id != null) return this.id.compareTo(((PaymentMethod) other).id); else return 0; } }