diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index 7dd00d0554..d37877b006 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -722,6 +722,7 @@ message PaymentAccountPayload { WeChatPayAccountPayload we_chat_pay_account_payload = 22; MoneyGramAccountPayload money_gram_account_payload = 23; HalCashAccountPayload hal_cash_account_payload = 24; + PromptPayAccountPayload prompt_pay_account_payload = 25; } map exclude_from_json_data = 15; } @@ -896,6 +897,10 @@ message F2FAccountPayload { string extra_info = 3; } +message PromptPayAccountPayload { + string prompt_pay_id = 1; +} + /////////////////////////////////////////////////////////////////////////////////////////// // PersistableEnvelope /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/offer/availability/ArbitratorSelection.java b/core/src/main/java/bisq/core/offer/availability/ArbitratorSelection.java index 6e9673ac51..259f11ca4c 100644 --- a/core/src/main/java/bisq/core/offer/availability/ArbitratorSelection.java +++ b/core/src/main/java/bisq/core/offer/availability/ArbitratorSelection.java @@ -32,6 +32,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -72,6 +73,7 @@ public class ArbitratorSelection { List lastAddressesUsedInTrades = list.stream() .filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap() != null) .map(tradeStatistics2 -> tradeStatistics2.getExtraDataMap().get(TradeStatistics2.ARBITRATOR_ADDRESS)) + .filter(Objects::nonNull) .collect(Collectors.toList()); Set arbitrators = arbitratorManager.getArbitratorsObservableMap().values().stream() diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java b/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java index df8a96b933..72a0ee667e 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java @@ -76,6 +76,8 @@ public class PaymentAccountFactory { return new HalCashAccount(); case PaymentMethod.F2F_ID: return new F2FAccount(); + case PaymentMethod.PROMPT_PAY_ID: + return new PromptPayAccount(); default: throw new RuntimeException("Not supported PaymentMethod: " + paymentMethod); } diff --git a/core/src/main/java/bisq/core/payment/PromptPayAccount.java b/core/src/main/java/bisq/core/payment/PromptPayAccount.java new file mode 100644 index 0000000000..89ed2102d3 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/PromptPayAccount.java @@ -0,0 +1,46 @@ +/* + * 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; + +import bisq.core.locale.FiatCurrency; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.payload.PaymentMethod; +import bisq.core.payment.payload.PromptPayAccountPayload; + +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +public final class PromptPayAccount extends PaymentAccount { + public PromptPayAccount() { + super(PaymentMethod.PROMPT_PAY); + setSingleTradeCurrency(new FiatCurrency("THB")); + } + + @Override + protected PaymentAccountPayload createPayload() { + return new PromptPayAccountPayload(paymentMethod.getId(), id); + } + + public void setPromptPayId(String promptPayId) { + ((PromptPayAccountPayload) paymentAccountPayload).setPromptPayId(promptPayId); + } + + public String getPromptPayId() { + return ((PromptPayAccountPayload) paymentAccountPayload).getPromptPayId(); + } +} diff --git a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java index e42ddbd8d4..05b495a961 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -81,6 +81,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable { 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 final String PROMPT_PAY_ID = "PROMPT_PAY"; @Deprecated public static PaymentMethod OK_PAY; @@ -112,6 +113,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable { public static PaymentMethod F2F; public static PaymentMethod HAL_CASH; public static PaymentMethod BLOCK_CHAINS; + public static PaymentMethod PROMPT_PAY; private static List ALL_VALUES; @@ -227,6 +229,9 @@ public final class PaymentMethod implements PersistablePayload, Comparable { ALI_PAY = new PaymentMethod(ALI_PAY_ID, DAY, maxTradeLimitLowRisk), WECHAT_PAY = new PaymentMethod(WECHAT_PAY_ID, DAY, maxTradeLimitLowRisk), + // Thailand + PROMPT_PAY = new PaymentMethod(PROMPT_PAY_ID, DAY, maxTradeLimitLowRisk), + // Altcoins BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, DAY, maxTradeLimitVeryLowRisk) )); diff --git a/core/src/main/java/bisq/core/payment/payload/PromptPayAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/PromptPayAccountPayload.java new file mode 100644 index 0000000000..37544654ab --- /dev/null +++ b/core/src/main/java/bisq/core/payment/payload/PromptPayAccountPayload.java @@ -0,0 +1,103 @@ +/* + * 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 io.bisq.generated.protobuffer.PB; + +import com.google.protobuf.Message; + +import org.springframework.util.CollectionUtils; + +import java.nio.charset.Charset; + +import java.util.HashMap; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +@EqualsAndHashCode(callSuper = true) +@ToString +@Setter +@Getter +@Slf4j +public final class PromptPayAccountPayload extends PaymentAccountPayload { + private String promptPayId = ""; + + public PromptPayAccountPayload(String paymentMethod, String id) { + super(paymentMethod, id); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private PromptPayAccountPayload(String paymentMethod, String id, + String promptPayId, + long maxTradePeriod, + @Nullable Map excludeFromJsonDataMap) { + super(paymentMethod, + id, + maxTradePeriod, + excludeFromJsonDataMap); + + this.promptPayId = promptPayId; + } + + @Override + public Message toProtoMessage() { + return getPaymentAccountPayloadBuilder() + .setPromptPayAccountPayload(PB.PromptPayAccountPayload.newBuilder() + .setPromptPayId(promptPayId)) + .build(); + } + + public static PromptPayAccountPayload fromProto(PB.PaymentAccountPayload proto) { + return new PromptPayAccountPayload(proto.getPaymentMethodId(), + proto.getId(), + proto.getPromptPayAccountPayload().getPromptPayId(), + proto.getMaxTradePeriod(), + CollectionUtils.isEmpty(proto.getExcludeFromJsonDataMap()) ? null : new HashMap<>(proto.getExcludeFromJsonDataMap())); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public String getPaymentDetails() { + return "PromptPay ID: " + promptPayId; + } + + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + + @Override + public byte[] getAgeWitnessInputData() { + return super.getAgeWitnessInputData(promptPayId.getBytes(Charset.forName("UTF-8"))); + } +} diff --git a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java index 29143bbd84..6a0ee3f2f5 100644 --- a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java @@ -37,6 +37,7 @@ import bisq.core.payment.payload.OKPayAccountPayload; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.payment.payload.PerfectMoneyAccountPayload; import bisq.core.payment.payload.PopmoneyAccountPayload; +import bisq.core.payment.payload.PromptPayAccountPayload; import bisq.core.payment.payload.RevolutAccountPayload; import bisq.core.payment.payload.SameBankAccountPayload; import bisq.core.payment.payload.SepaAccountPayload; @@ -135,6 +136,8 @@ public class CoreProtoResolver implements ProtoResolver { return HalCashAccountPayload.fromProto(proto); case U_S_POSTAL_MONEY_ORDER_ACCOUNT_PAYLOAD: return USPostalMoneyOrderAccountPayload.fromProto(proto); + case PROMPT_PAY_ACCOUNT_PAYLOAD: + return PromptPayAccountPayload.fromProto(proto); default: throw new ProtobufferRuntimeException("Unknown proto message case(PB.PaymentAccountPayload). messageCase=" + messageCase); } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 9c8efad41a..db1176f5fe 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2132,6 +2132,7 @@ payment.moneyBeam.accountId=Email or phone no. payment.venmo.venmoUserName=Venmo username payment.popmoney.accountId=Email or phone no. payment.revolut.accountId=Email or phone no. +payment.promptPay.promptPayId=Citizen ID/Tax ID or phone no. payment.supportedCurrencies=Supported currencies payment.limitations=Limitations payment.salt=Salt for account age verification @@ -2219,6 +2220,9 @@ payment.limits.info=Please be aware that all bank transfers carry a certain amou \n\ Please note that there are no limits on the total number of times you can trade. +payment.cashDeposit.info=Please confirm your bank allows you to send cash deposits into other peoples' accounts. \ + For example, Bank of America and Wells Fargo no longer allow such deposits. + payment.f2f.contact=Contact info payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...) payment.f2f.city=City for 'Face to face' meeting @@ -2312,6 +2316,8 @@ INTERAC_E_TRANSFER=Interac e-Transfer HAL_CASH=HalCash # suppress inspection "UnusedProperty" BLOCK_CHAINS=Altcoins +# suppress inspection "UnusedProperty" +PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" OK_PAY_SHORT=OKPay @@ -2351,6 +2357,8 @@ INTERAC_E_TRANSFER_SHORT=Interac e-Transfer HAL_CASH_SHORT=HalCash # suppress inspection "UnusedProperty" BLOCK_CHAINS_SHORT=Altcoins +# suppress inspection "UnusedProperty" +PROMPT_PAY_SHORT=PromptPay #################################################################### diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PromptPayForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PromptPayForm.java new file mode 100644 index 0000000000..3fac750eef --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PromptPayForm.java @@ -0,0 +1,106 @@ +/* + * 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.desktop.components.paymentmethods; + +import bisq.desktop.components.InputTextField; +import bisq.desktop.util.Layout; +import bisq.desktop.util.validation.PromptPayValidator; + +import bisq.core.locale.Res; +import bisq.core.locale.TradeCurrency; +import bisq.core.payment.AccountAgeWitnessService; +import bisq.core.payment.PaymentAccount; +import bisq.core.payment.PromptPayAccount; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.payload.PromptPayAccountPayload; +import bisq.core.util.BSFormatter; +import bisq.core.util.validation.InputValidator; + +import javafx.scene.control.TextField; +import javafx.scene.layout.GridPane; + +import static bisq.desktop.util.FormBuilder.addInputTextField; +import static bisq.desktop.util.FormBuilder.addTextField; +import static bisq.desktop.util.FormBuilder.addTopLabelTextField; + +public class PromptPayForm extends PaymentMethodForm { + private final PromptPayAccount promptPayAccount; + private final PromptPayValidator promptPayValidator; + private InputTextField promptPayIdInputTextField; + + public static int addFormForBuyer(GridPane gridPane, int gridRow, + PaymentAccountPayload paymentAccountPayload) { + addTopLabelTextField(gridPane, ++gridRow, Res.get("payment.promptPay.promptPayId"), + ((PromptPayAccountPayload) paymentAccountPayload).getPromptPayId()); + return gridRow; + } + + public PromptPayForm(PaymentAccount paymentAccount, AccountAgeWitnessService accountAgeWitnessService, PromptPayValidator promptPayValidator, + InputValidator inputValidator, GridPane gridPane, int gridRow, BSFormatter formatter) { + super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter); + this.promptPayAccount = (PromptPayAccount) paymentAccount; + this.promptPayValidator = promptPayValidator; + } + + @Override + public void addFormForAddAccount() { + gridRowFrom = gridRow + 1; + + promptPayIdInputTextField = addInputTextField(gridPane, ++gridRow, + Res.get("payment.promptPay.promptPayId")); + promptPayIdInputTextField.setValidator(promptPayValidator); + promptPayIdInputTextField.textProperty().addListener((ov, oldValue, newValue) -> { + promptPayAccount.setPromptPayId(newValue); + updateFromInputs(); + }); + + TradeCurrency singleTradeCurrency = promptPayAccount.getSingleTradeCurrency(); + String nameAndCode = singleTradeCurrency != null ? singleTradeCurrency.getNameAndCode() : "null"; + addTextField(gridPane, ++gridRow, Res.getWithCol("shared.currency"), nameAndCode); + addLimitations(); + addAccountNameTextFieldWithAutoFillToggleButton(); + } + + @Override + protected void autoFillNameTextField() { + setAccountNameWithString(promptPayIdInputTextField.getText()); + } + + @Override + public void addFormForDisplayAccount() { + gridRowFrom = gridRow; + addTextField(gridPane, gridRow, Res.get("payment.account.name"), + promptPayAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); + addTextField(gridPane, ++gridRow, Res.getWithCol("shared.paymentMethod"), + Res.get(promptPayAccount.getPaymentMethod().getId())); + TextField field = addTextField(gridPane, ++gridRow, Res.get("payment.promptPay.promptPayId"), + promptPayAccount.getPromptPayId()); + field.setMouseTransparent(false); + TradeCurrency singleTradeCurrency = promptPayAccount.getSingleTradeCurrency(); + String nameAndCode = singleTradeCurrency != null ? singleTradeCurrency.getNameAndCode() : "null"; + addTextField(gridPane, ++gridRow, Res.getWithCol("shared.currency"), nameAndCode); + addLimitations(); + } + + @Override + public void updateAllInputsValid() { + allInputsValid.set(isAccountNameValid() + && promptPayValidator.validate(promptPayAccount.getPromptPayId()).isValid + && promptPayAccount.getTradeCurrencies().size() > 0); + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java index 52347a5a11..d487d6c481 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java @@ -38,6 +38,7 @@ import bisq.desktop.components.paymentmethods.OKPayForm; import bisq.desktop.components.paymentmethods.PaymentMethodForm; import bisq.desktop.components.paymentmethods.PerfectMoneyForm; import bisq.desktop.components.paymentmethods.PopmoneyForm; +import bisq.desktop.components.paymentmethods.PromptPayForm; import bisq.desktop.components.paymentmethods.RevolutForm; import bisq.desktop.components.paymentmethods.SameBankForm; import bisq.desktop.components.paymentmethods.SepaForm; @@ -67,6 +68,7 @@ import bisq.desktop.util.validation.MoneyBeamValidator; import bisq.desktop.util.validation.OKPayValidator; import bisq.desktop.util.validation.PerfectMoneyValidator; import bisq.desktop.util.validation.PopmoneyValidator; +import bisq.desktop.util.validation.PromptPayValidator; import bisq.desktop.util.validation.RevolutValidator; import bisq.desktop.util.validation.SwishValidator; import bisq.desktop.util.validation.USPostalMoneyOrderValidator; @@ -77,6 +79,7 @@ import bisq.desktop.util.validation.WeChatPayValidator; import bisq.core.app.BisqEnvironment; import bisq.core.locale.Res; import bisq.core.payment.AccountAgeWitnessService; +import bisq.core.payment.CashDepositAccount; import bisq.core.payment.ClearXchangeAccount; import bisq.core.payment.F2FAccount; import bisq.core.payment.HalCashAccount; @@ -147,6 +150,7 @@ public class FiatAccountsView extends ActivatableViewAndModel paymentAccountsListView; @@ -179,6 +183,7 @@ public class FiatAccountsView extends ActivatableViewAndModel doSaveNewAccount(paymentAccount)) .show(); + } else if (paymentAccount instanceof CashDepositAccount) { + new Popup<>().information(Res.get("payment.cashDeposit.info")) + .width(700) + .closeButtonText(Res.get("shared.cancel")) + .actionButtonText(Res.get("shared.iConfirm")) + .onAction(() -> doSaveNewAccount(paymentAccount)) + .show(); } else { doSaveNewAccount(paymentAccount); } @@ -515,6 +528,8 @@ public class FiatAccountsView extends ActivatableViewAndModel. + */ + +package bisq.desktop.util.validation; + +import bisq.core.util.validation.InputValidator; + +public final class PromptPayValidator extends InputValidator { + + @Override + public ValidationResult validate(String input) { + // TODO + return super.validate(input); + } +}