diff --git a/core/src/main/java/haveno/core/offer/Offer.java b/core/src/main/java/haveno/core/offer/Offer.java index 3dc566b308..1ff5eb11be 100644 --- a/core/src/main/java/haveno/core/offer/Offer.java +++ b/core/src/main/java/haveno/core/offer/Offer.java @@ -432,6 +432,12 @@ else if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload return getExtraDataMap().get(OfferPayload.PAYPAL_EXTRA_INFO); else if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload.CASHAPP_EXTRA_INFO)) return getExtraDataMap().get(OfferPayload.CASHAPP_EXTRA_INFO); + else if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload.ZELLE_EXTRA_INFO)) + return getExtraDataMap().get(OfferPayload.ZELLE_EXTRA_INFO); + else if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload.SEPA_EXTRA_INFO)) + return getExtraDataMap().get(OfferPayload.SEPA_EXTRA_INFO); + else if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload.SEPA_INSTANT_EXTRA_INFO)) + return getExtraDataMap().get(OfferPayload.SEPA_INSTANT_EXTRA_INFO); else return ""; } diff --git a/core/src/main/java/haveno/core/offer/OfferPayload.java b/core/src/main/java/haveno/core/offer/OfferPayload.java index 0dadf882ba..df9bc2112b 100644 --- a/core/src/main/java/haveno/core/offer/OfferPayload.java +++ b/core/src/main/java/haveno/core/offer/OfferPayload.java @@ -102,6 +102,9 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay public static final String PAY_BY_MAIL_EXTRA_INFO = "payByMailExtraInfo"; public static final String AUSTRALIA_PAYID_EXTRA_INFO = "australiaPayidExtraInfo"; public static final String PAYPAL_EXTRA_INFO = "payPalExtraInfo"; + public static final String ZELLE_EXTRA_INFO = "zelleExtraInfo"; + public static final String SEPA_EXTRA_INFO = "sepaExtraInfo"; + public static final String SEPA_INSTANT_EXTRA_INFO = "sepaInstantExtraInfo"; // Comma separated list of ordinal of a haveno.common.app.Capability. E.g. ordinal of // Capability.SIGNED_ACCOUNT_AGE_WITNESS is 11 and Capability.MEDIATION is 12 so if we want to signal that maker diff --git a/core/src/main/java/haveno/core/offer/OfferUtil.java b/core/src/main/java/haveno/core/offer/OfferUtil.java index 3d4c1c376e..ee995251ef 100644 --- a/core/src/main/java/haveno/core/offer/OfferUtil.java +++ b/core/src/main/java/haveno/core/offer/OfferUtil.java @@ -42,6 +42,9 @@ import static haveno.core.offer.OfferPayload.F2F_EXTRA_INFO; import static haveno.core.offer.OfferPayload.PAY_BY_MAIL_EXTRA_INFO; import static haveno.core.offer.OfferPayload.PAYPAL_EXTRA_INFO; +import static haveno.core.offer.OfferPayload.SEPA_EXTRA_INFO; +import static haveno.core.offer.OfferPayload.SEPA_INSTANT_EXTRA_INFO; +import static haveno.core.offer.OfferPayload.ZELLE_EXTRA_INFO; import static haveno.core.offer.OfferPayload.REFERRAL_ID; import static haveno.core.offer.OfferPayload.XMR_AUTO_CONF; import static haveno.core.offer.OfferPayload.XMR_AUTO_CONF_ENABLED_VALUE; @@ -52,6 +55,9 @@ import haveno.core.payment.PayByMailAccount; import haveno.core.payment.PayPalAccount; import haveno.core.payment.PaymentAccount; +import haveno.core.payment.SepaAccount; +import haveno.core.payment.SepaInstantAccount; +import haveno.core.payment.ZelleAccount; import haveno.core.provider.price.MarketPrice; import haveno.core.provider.price.PriceFeedService; import haveno.core.trade.statistics.ReferralIdService; @@ -217,6 +223,18 @@ public Map<String, String> getExtraDataMap(PaymentAccount paymentAccount, extraDataMap.put(AUSTRALIA_PAYID_EXTRA_INFO, ((AustraliaPayidAccount) paymentAccount).getExtraInfo()); } + if (paymentAccount instanceof ZelleAccount) { + extraDataMap.put(ZELLE_EXTRA_INFO, ((ZelleAccount) paymentAccount).getExtraInfo()); + } + + if (paymentAccount instanceof SepaAccount) { + extraDataMap.put(SEPA_EXTRA_INFO, ((SepaAccount) paymentAccount).getExtraInfo()); + } + + if (paymentAccount instanceof SepaInstantAccount) { + extraDataMap.put(SEPA_INSTANT_EXTRA_INFO, ((SepaInstantAccount) paymentAccount).getExtraInfo()); + } + extraDataMap.put(CAPABILITIES, Capabilities.app.toStringList()); if (currencyCode.equals("XMR") && direction == OfferDirection.SELL) { diff --git a/core/src/main/java/haveno/core/payment/SepaAccount.java b/core/src/main/java/haveno/core/payment/SepaAccount.java index 1ad984458c..17b561df44 100644 --- a/core/src/main/java/haveno/core/payment/SepaAccount.java +++ b/core/src/main/java/haveno/core/payment/SepaAccount.java @@ -43,6 +43,7 @@ public final class SepaAccount extends CountryBasedPaymentAccount implements Ban PaymentAccountFormField.FieldId.BIC, PaymentAccountFormField.FieldId.COUNTRY, PaymentAccountFormField.FieldId.ACCEPTED_COUNTRY_CODES, + PaymentAccountFormField.FieldId.EXTRA_INFO, PaymentAccountFormField.FieldId.SALT ); @@ -104,6 +105,14 @@ public void removeAcceptedCountry(String countryCode) { ((SepaAccountPayload) paymentAccountPayload).removeAcceptedCountry(countryCode); } + public void setExtraInfo(String extraInfo) { + ((SepaAccountPayload) paymentAccountPayload).setExtraInfo(extraInfo); + } + + public String getExtraInfo() { + return ((SepaAccountPayload) paymentAccountPayload).getExtraInfo(); + } + @Override public void onPersistChanges() { super.onPersistChanges(); diff --git a/core/src/main/java/haveno/core/payment/SepaInstantAccount.java b/core/src/main/java/haveno/core/payment/SepaInstantAccount.java index d006aa84b7..3db7849a58 100644 --- a/core/src/main/java/haveno/core/payment/SepaInstantAccount.java +++ b/core/src/main/java/haveno/core/payment/SepaInstantAccount.java @@ -25,6 +25,7 @@ import haveno.core.locale.TradeCurrency; import haveno.core.payment.payload.PaymentAccountPayload; import haveno.core.payment.payload.PaymentMethod; +import haveno.core.payment.payload.SepaAccountPayload; import haveno.core.payment.payload.SepaInstantAccountPayload; import haveno.core.payment.validation.SepaIBANValidator; import lombok.EqualsAndHashCode; @@ -90,6 +91,15 @@ public void removeAcceptedCountry(String countryCode) { ((SepaInstantAccountPayload) paymentAccountPayload).removeAcceptedCountry(countryCode); } + + public void setExtraInfo(String extraInfo) { + ((SepaAccountPayload) paymentAccountPayload).setExtraInfo(extraInfo); + } + + public String getExtraInfo() { + return ((SepaAccountPayload) paymentAccountPayload).getExtraInfo(); + } + @Override public void onPersistChanges() { super.onPersistChanges(); diff --git a/core/src/main/java/haveno/core/payment/ZelleAccount.java b/core/src/main/java/haveno/core/payment/ZelleAccount.java index 4415f4ed42..2f268548dc 100644 --- a/core/src/main/java/haveno/core/payment/ZelleAccount.java +++ b/core/src/main/java/haveno/core/payment/ZelleAccount.java @@ -42,6 +42,7 @@ public ZelleAccount() { PaymentAccountFormField.FieldId.ACCOUNT_NAME, PaymentAccountFormField.FieldId.HOLDER_NAME, PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR, + PaymentAccountFormField.FieldId.EXTRA_INFO, PaymentAccountFormField.FieldId.SALT ); @@ -75,4 +76,12 @@ public void setHolderName(String holderName) { public String getHolderName() { return ((ZelleAccountPayload) paymentAccountPayload).getHolderName(); } + + public void setExtraInfo(String extraInfo) { + ((ZelleAccountPayload) paymentAccountPayload).setExtraInfo(extraInfo); + } + + public String getExtraInfo() { + return ((ZelleAccountPayload) paymentAccountPayload).getExtraInfo(); + } } diff --git a/core/src/main/java/haveno/core/payment/payload/SepaAccountPayload.java b/core/src/main/java/haveno/core/payment/payload/SepaAccountPayload.java index 3446a979e8..901c6edc09 100644 --- a/core/src/main/java/haveno/core/payment/payload/SepaAccountPayload.java +++ b/core/src/main/java/haveno/core/payment/payload/SepaAccountPayload.java @@ -47,6 +47,8 @@ public final class SepaAccountPayload extends CountryBasedPaymentAccountPayload @Setter private String bic = ""; private String email = ""; // not used anymore but need to keep it for backward compatibility, must not be null but empty string, otherwise hash check fails for contract + @Setter + private String extraInfo = ""; // Don't use a set here as we need a deterministic ordering, otherwise the contract hash does not match private final List<String> persistedAcceptedCountryCodes = new ArrayList<>(); @@ -73,6 +75,7 @@ private SepaAccountPayload(String paymentMethodName, String iban, String bic, String email, + String extraInfo, long maxTradePeriod, Map<String, String> excludeFromJsonDataMap) { super(paymentMethodName, @@ -87,6 +90,7 @@ private SepaAccountPayload(String paymentMethodName, this.bic = bic; this.email = email; this.acceptedCountryCodes = acceptedCountryCodes; + this.extraInfo = extraInfo; persistedAcceptedCountryCodes.addAll(acceptedCountryCodes); } @@ -97,7 +101,8 @@ public Message toProtoMessage() { .setHolderName(holderName) .setIban(iban) .setBic(bic) - .setEmail(email); + .setEmail(email) + .setExtraInfo(extraInfo); final protobuf.CountryBasedPaymentAccountPayload.Builder countryBasedPaymentAccountPayload = getPaymentAccountPayloadBuilder() .getCountryBasedPaymentAccountPayloadBuilder() .setSepaAccountPayload(builder); @@ -117,6 +122,7 @@ public static PaymentAccountPayload fromProto(protobuf.PaymentAccountPayload pro sepaAccountPayloadPB.getIban(), sepaAccountPayloadPB.getBic(), sepaAccountPayloadPB.getEmail(), + sepaAccountPayloadPB.getExtraInfo(), proto.getMaxTradePeriod(), new HashMap<>(proto.getExcludeFromJsonDataMap())); } @@ -154,7 +160,8 @@ public void revertChanges() { @Override public String getPaymentDetails() { return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.owner") + " " + holderName + ", IBAN: " + - iban + ", BIC: " + bic + ", " + Res.getWithCol("payment.bank.country") + " " + getCountryCode(); + iban + ", BIC: " + bic + ", " + Res.getWithCol("payment.bank.country") + " " + getCountryCode() + ", " + + Res.getWithCol("payment.shared.extraInfo") + " " + extraInfo; } @Override @@ -162,7 +169,8 @@ public String getPaymentDetailsForTradePopup() { return Res.getWithCol("payment.account.owner") + " " + holderName + "\n" + "IBAN: " + iban + "\n" + "BIC: " + bic + "\n" + - Res.getWithCol("payment.bank.country") + " " + CountryUtil.getNameByCode(countryCode); + Res.getWithCol("payment.bank.country") + " " + CountryUtil.getNameByCode(countryCode) + "\n" + + Res.getWithCol("payment.shared.extraInfo") + " " + extraInfo; } @Override diff --git a/core/src/main/java/haveno/core/payment/payload/SepaInstantAccountPayload.java b/core/src/main/java/haveno/core/payment/payload/SepaInstantAccountPayload.java index 66a5c1062a..e5fbbda4dd 100644 --- a/core/src/main/java/haveno/core/payment/payload/SepaInstantAccountPayload.java +++ b/core/src/main/java/haveno/core/payment/payload/SepaInstantAccountPayload.java @@ -46,6 +46,7 @@ public final class SepaInstantAccountPayload extends CountryBasedPaymentAccountP private String iban = ""; @Setter private String bic = ""; + private String extraInfo = ""; // Don't use a set here as we need a deterministic ordering, otherwise the contract hash does not match private final List<String> persistedAcceptedCountryCodes = new ArrayList<>(); @@ -71,6 +72,7 @@ private SepaInstantAccountPayload(String paymentMethodName, String holderName, String iban, String bic, + String extraInfo, long maxTradePeriod, Map<String, String> excludeFromJsonDataMap) { super(paymentMethodName, @@ -83,6 +85,7 @@ private SepaInstantAccountPayload(String paymentMethodName, this.holderName = holderName; this.iban = iban; this.bic = bic; + this.extraInfo = extraInfo; persistedAcceptedCountryCodes.addAll(acceptedCountryCodes); } @@ -92,7 +95,8 @@ public Message toProtoMessage() { protobuf.SepaInstantAccountPayload.newBuilder() .setHolderName(holderName) .setIban(iban) - .setBic(bic); + .setBic(bic) + .setExtraInfo(extraInfo); final protobuf.CountryBasedPaymentAccountPayload.Builder countryBasedPaymentAccountPayload = getPaymentAccountPayloadBuilder() .getCountryBasedPaymentAccountPayloadBuilder() .setSepaInstantAccountPayload(builder); @@ -111,6 +115,7 @@ public static PaymentAccountPayload fromProto(protobuf.PaymentAccountPayload pro sepaInstantAccountPayloadPB.getHolderName(), sepaInstantAccountPayloadPB.getIban(), sepaInstantAccountPayloadPB.getBic(), + sepaInstantAccountPayloadPB.getExtraInfo(), proto.getMaxTradePeriod(), new HashMap<>(proto.getExcludeFromJsonDataMap())); } @@ -142,7 +147,8 @@ public void revertChanges() { @Override public String getPaymentDetails() { return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.owner") + " " + holderName + ", IBAN: " + - iban + ", BIC: " + bic + ", " + Res.getWithCol("payment.bank.country") + " " + getCountryCode(); + iban + ", BIC: " + bic + ", " + Res.getWithCol("payment.bank.country") + " " + getCountryCode() + ", " + + Res.getWithCol("payment.shared.extraInfo") + " " + extraInfo; } @Override @@ -150,7 +156,8 @@ public String getPaymentDetailsForTradePopup() { return Res.getWithCol("payment.account.owner") + " " + holderName + "\n" + "IBAN: " + iban + "\n" + "BIC: " + bic + "\n" + - Res.getWithCol("payment.bank.country") + " " + CountryUtil.getNameByCode(countryCode); + Res.getWithCol("payment.bank.country") + " " + CountryUtil.getNameByCode(countryCode) + "\n" + + Res.getWithCol("payment.shared.extraInfo") + " " + extraInfo; } @Override diff --git a/core/src/main/java/haveno/core/payment/payload/ZelleAccountPayload.java b/core/src/main/java/haveno/core/payment/payload/ZelleAccountPayload.java index 86c94abd36..ddc720a756 100644 --- a/core/src/main/java/haveno/core/payment/payload/ZelleAccountPayload.java +++ b/core/src/main/java/haveno/core/payment/payload/ZelleAccountPayload.java @@ -37,6 +37,7 @@ public final class ZelleAccountPayload extends PaymentAccountPayload implements PayloadWithHolderName { private String emailOrMobileNr = ""; private String holderName = ""; + private String extraInfo = ""; public ZelleAccountPayload(String paymentMethod, String id) { super(paymentMethod, id); @@ -51,6 +52,7 @@ private ZelleAccountPayload(String paymentMethod, String id, String emailOrMobileNr, String holderName, + String extraInfo, long maxTradePeriod, Map<String, String> excludeFromJsonDataMap) { super(paymentMethod, @@ -60,6 +62,7 @@ private ZelleAccountPayload(String paymentMethod, this.emailOrMobileNr = emailOrMobileNr; this.holderName = holderName; + this.extraInfo = extraInfo; } @Override @@ -67,7 +70,8 @@ public Message toProtoMessage() { return getPaymentAccountPayloadBuilder() .setZelleAccountPayload(protobuf.ZelleAccountPayload.newBuilder() .setEmailOrMobileNr(emailOrMobileNr) - .setHolderName(holderName)) + .setHolderName(holderName) + .setExtraInfo(extraInfo)) .build(); } @@ -76,6 +80,7 @@ public static ZelleAccountPayload fromProto(protobuf.PaymentAccountPayload proto proto.getId(), proto.getZelleAccountPayload().getEmailOrMobileNr(), proto.getZelleAccountPayload().getHolderName(), + proto.getZelleAccountPayload().getExtraInfo(), proto.getMaxTradePeriod(), new HashMap<>(proto.getExcludeFromJsonDataMap())); } @@ -88,13 +93,15 @@ public static ZelleAccountPayload fromProto(protobuf.PaymentAccountPayload proto @Override public String getPaymentDetails() { return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.owner") + " " + holderName + ", " + - Res.getWithCol("payment.emailOrMobile") + " " + emailOrMobileNr; + Res.getWithCol("payment.emailOrMobile") + " " + emailOrMobileNr + ", " + + Res.getWithCol("payment.shared.extraInfo") + " " + extraInfo; } @Override public String getPaymentDetailsForTradePopup() { return Res.getWithCol("payment.account.owner") + " " + holderName + "\n" + - Res.getWithCol("payment.emailOrMobile") + " " + emailOrMobileNr; + Res.getWithCol("payment.emailOrMobile") + " " + emailOrMobileNr + "\n" + + Res.getWithCol("payment.shared.extraInfo") + " " + extraInfo; } @Override diff --git a/desktop/src/main/java/haveno/desktop/components/paymentmethods/SepaForm.java b/desktop/src/main/java/haveno/desktop/components/paymentmethods/SepaForm.java index 526d9d37ff..0726dfad3d 100644 --- a/desktop/src/main/java/haveno/desktop/components/paymentmethods/SepaForm.java +++ b/desktop/src/main/java/haveno/desktop/components/paymentmethods/SepaForm.java @@ -35,14 +35,19 @@ import haveno.desktop.util.normalization.IBANNormalizer; import javafx.collections.FXCollections; import javafx.scene.control.ComboBox; +import javafx.scene.control.TextArea; import javafx.scene.control.TextFormatter; import javafx.scene.layout.GridPane; import java.util.List; import java.util.Optional; +import com.jfoenix.controls.JFXTextArea; + +import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextArea; import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextField; import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; +import static haveno.desktop.util.FormBuilder.addTopLabelTextArea; public class SepaForm extends GeneralSepaForm { @@ -60,6 +65,10 @@ public static int addFormForBuyer(GridPane gridPane, int gridRow, // IBAN, BIC will not be translated addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, IBAN, sepaAccountPayload.getIban()); addCompactTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, BIC, sepaAccountPayload.getBic()); + TextArea textExtraInfo = addCompactTopLabelTextArea(gridPane, ++gridRow, Res.get("payment.shared.extraInfo"), "").second; + textExtraInfo.setMinHeight(70); + textExtraInfo.setEditable(false); + textExtraInfo.setText(((SepaAccountPayload) paymentAccountPayload).getExtraInfo()); return gridRow; } @@ -141,6 +150,15 @@ public void addFormForAddAccount() { ibanInputTextField.refreshValidation(); }); + TextArea extraTextArea = addTopLabelTextArea(gridPane, ++gridRow, + Res.get("payment.shared.optionalExtra"), Res.get("payment.shared.extraInfo.prompt")).second; + extraTextArea.setMinHeight(70); + ((JFXTextArea) extraTextArea).setLabelFloat(false); + extraTextArea.textProperty().addListener((ov, oldValue, newValue) -> { + sepaAccount.setExtraInfo(newValue); + updateFromInputs(); + }); + updateFromInputs(); } @@ -172,6 +190,12 @@ public void addFormForEditAccount() { addCountriesGrid(Res.get("payment.accept.euro"), CountryUtil.getAllSepaEuroCountries()); addCountriesGrid(Res.get("payment.accept.nonEuro"), CountryUtil.getAllSepaNonEuroCountries()); + + TextArea textAreaExtra = addCompactTopLabelTextArea(gridPane, ++gridRow, Res.get("payment.shared.extraInfo"), "").second; + textAreaExtra.setText(sepaAccount.getExtraInfo()); + textAreaExtra.setMinHeight(70); + textAreaExtra.setEditable(false); + addLimitations(true); } diff --git a/desktop/src/main/java/haveno/desktop/components/paymentmethods/SepaInstantForm.java b/desktop/src/main/java/haveno/desktop/components/paymentmethods/SepaInstantForm.java index 380f31accb..b0031161f9 100644 --- a/desktop/src/main/java/haveno/desktop/components/paymentmethods/SepaInstantForm.java +++ b/desktop/src/main/java/haveno/desktop/components/paymentmethods/SepaInstantForm.java @@ -25,6 +25,7 @@ import haveno.core.payment.PaymentAccount; import haveno.core.payment.SepaInstantAccount; import haveno.core.payment.payload.PaymentAccountPayload; +import haveno.core.payment.payload.SepaAccountPayload; import haveno.core.payment.payload.SepaInstantAccountPayload; import haveno.core.payment.validation.BICValidator; import haveno.core.payment.validation.SepaIBANValidator; @@ -35,14 +36,19 @@ import haveno.desktop.util.normalization.IBANNormalizer; import javafx.collections.FXCollections; import javafx.scene.control.ComboBox; +import javafx.scene.control.TextArea; import javafx.scene.control.TextFormatter; import javafx.scene.layout.GridPane; import java.util.List; import java.util.Optional; +import com.jfoenix.controls.JFXTextArea; + +import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextArea; import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextField; import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; +import static haveno.desktop.util.FormBuilder.addTopLabelTextArea; public class SepaInstantForm extends GeneralSepaForm { @@ -60,6 +66,10 @@ public static int addFormForBuyer(GridPane gridPane, int gridRow, // IBAN, BIC will not be translated addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, IBAN, sepaInstantAccountPayload.getIban()); addCompactTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, BIC, sepaInstantAccountPayload.getBic()); + TextArea textExtraInfo = addCompactTopLabelTextArea(gridPane, ++gridRow, Res.get("payment.shared.extraInfo"), "").second; + textExtraInfo.setMinHeight(70); + textExtraInfo.setEditable(false); + textExtraInfo.setText(((SepaAccountPayload) paymentAccountPayload).getExtraInfo()); return gridRow; } @@ -143,6 +153,15 @@ public void addFormForAddAccount() { ibanInputTextField.refreshValidation(); }); + TextArea extraTextArea = addTopLabelTextArea(gridPane, ++gridRow, + Res.get("payment.shared.optionalExtra"), Res.get("payment.shared.extraInfo.prompt")).second; + extraTextArea.setMinHeight(70); + ((JFXTextArea) extraTextArea).setLabelFloat(false); + extraTextArea.textProperty().addListener((ov, oldValue, newValue) -> { + sepaInstantAccount.setExtraInfo(newValue); + updateFromInputs(); + }); + updateFromInputs(); } @@ -174,6 +193,12 @@ public void addFormForEditAccount() { addCountriesGrid(Res.get("payment.accept.euro"), CountryUtil.getAllSepaEuroCountries()); addCountriesGrid(Res.get("payment.accept.nonEuro"), CountryUtil.getAllSepaNonEuroCountries()); + + TextArea textAreaExtra = addCompactTopLabelTextArea(gridPane, ++gridRow, Res.get("payment.shared.extraInfo"), "").second; + textAreaExtra.setText(sepaInstantAccount.getExtraInfo()); + textAreaExtra.setMinHeight(70); + textAreaExtra.setEditable(false); + addLimitations(true); } diff --git a/desktop/src/main/java/haveno/desktop/components/paymentmethods/ZelleForm.java b/desktop/src/main/java/haveno/desktop/components/paymentmethods/ZelleForm.java index 79dab45ee2..3f9069f630 100644 --- a/desktop/src/main/java/haveno/desktop/components/paymentmethods/ZelleForm.java +++ b/desktop/src/main/java/haveno/desktop/components/paymentmethods/ZelleForm.java @@ -17,6 +17,7 @@ package haveno.desktop.components.paymentmethods; +import com.jfoenix.controls.JFXTextArea; import haveno.core.account.witness.AccountAgeWitnessService; import haveno.core.locale.Res; import haveno.core.locale.TradeCurrency; @@ -29,11 +30,14 @@ import haveno.core.util.validation.InputValidator; import haveno.desktop.components.InputTextField; import haveno.desktop.util.FormBuilder; +import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; +import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextArea; import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextField; import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; +import static haveno.desktop.util.FormBuilder.addTopLabelTextArea; import static haveno.desktop.util.FormBuilder.addTopLabelTextField; public class ZelleForm extends PaymentMethodForm { @@ -45,6 +49,10 @@ public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccount ((ZelleAccountPayload) paymentAccountPayload).getHolderName()); addCompactTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, Res.get("payment.email.mobile"), ((ZelleAccountPayload) paymentAccountPayload).getEmailOrMobileNr()); + TextArea textExtraInfo = addCompactTopLabelTextArea(gridPane, ++gridRow, Res.get("payment.shared.extraInfo"), "").second; + textExtraInfo.setMinHeight(70); + textExtraInfo.setEditable(false); + textExtraInfo.setText(((ZelleAccountPayload) paymentAccountPayload).getExtraInfo()); return gridRow; } @@ -77,6 +85,16 @@ public void addFormForAddAccount() { final String nameAndCode = singleTradeCurrency != null ? singleTradeCurrency.getNameAndCode() : ""; addTopLabelTextField(gridPane, ++gridRow, Res.get("shared.currency"), nameAndCode); + + TextArea extraTextArea = addTopLabelTextArea(gridPane, ++gridRow, + Res.get("payment.shared.optionalExtra"), Res.get("payment.shared.extraInfo.prompt")).second; + extraTextArea.setMinHeight(70); + ((JFXTextArea) extraTextArea).setLabelFloat(false); + extraTextArea.textProperty().addListener((ov, oldValue, newValue) -> { + zelleAccount.setExtraInfo(newValue); + updateFromInputs(); + }); + addLimitations(false); addAccountNameTextFieldWithAutoFillToggleButton(); } @@ -101,6 +119,10 @@ public void addFormForEditAccount() { final String nameAndCode = singleTradeCurrency != null ? singleTradeCurrency.getNameAndCode() : ""; addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.currency"), nameAndCode); + TextArea textAreaExtra = addCompactTopLabelTextArea(gridPane, ++gridRow, Res.get("payment.shared.extraInfo"), "").second; + textAreaExtra.setText(zelleAccount.getExtraInfo()); + textAreaExtra.setMinHeight(70); + textAreaExtra.setEditable(false); addLimitations(true); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java index 1d662aebc7..e8029471d9 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java @@ -160,9 +160,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer private int gridRow = 0; private final HashMap<String, Boolean> paymentAccountWarningDisplayed = new HashMap<>(); - private boolean offerDetailsWindowDisplayed, zelleWarningDisplayed, fasterPaymentsWarningDisplayed, + private boolean offerDetailsWindowDisplayed, zelleWarningDisplayed, zelleExtraInfoDisplayed, fasterPaymentsWarningDisplayed, takeOfferFromUnsignedAccountWarningDisplayed, payByMailWarningDisplayed, cashAtAtmWarningDisplayed, - australiaPayidWarningDisplayed, paypalWarningDisplayed, cashAppWarningDisplayed; + australiaPayidWarningDisplayed, paypalWarningDisplayed, cashAppWarningDisplayed, sepaExtraInfoDisplayed, sepaInstantExtraInfoDisplayed; private SimpleBooleanProperty errorPopupDisplayed; private ChangeListener<Boolean> amountFocusedListener, getShowWalletFundedNotificationListener; @@ -268,7 +268,7 @@ protected void activate() { balanceTextField.setTargetAmount(model.dataModel.getTotalToPay().get()); maybeShowTakeOfferFromUnsignedAccountWarning(model.dataModel.getOffer()); - maybeShowZelleWarning(lastPaymentAccount); + maybeShowZelleWarning(lastPaymentAccount, model.dataModel.getOffer()); maybeShowFasterPaymentsWarning(lastPaymentAccount); maybeShowAccountWarning(lastPaymentAccount, model.dataModel.isBuyOffer()); maybeShowPayByMailWarning(lastPaymentAccount, model.dataModel.getOffer()); @@ -276,6 +276,8 @@ protected void activate() { maybeShowAustraliaPayidWarning(lastPaymentAccount, model.dataModel.getOffer()); maybeShowPayPalWarning(lastPaymentAccount, model.dataModel.getOffer()); maybeShowCashAppWarning(lastPaymentAccount, model.dataModel.getOffer()); + maybeShowSepaWarning(lastPaymentAccount, model.dataModel.getOffer()); + maybeShowSepaInstantWarning(lastPaymentAccount, model.dataModel.getOffer()); if (!model.isRange()) { nextButton.setVisible(false); @@ -759,7 +761,7 @@ private void addPaymentGroup() { paymentAccountsComboBox.setOnAction(e -> { PaymentAccount paymentAccount = paymentAccountsComboBox.getSelectionModel().getSelectedItem(); if (paymentAccount != null) { - maybeShowZelleWarning(paymentAccount); + maybeShowZelleWarning(paymentAccount, model.dataModel.getOffer()); maybeShowFasterPaymentsWarning(paymentAccount); maybeShowAccountWarning(paymentAccount, model.dataModel.isBuyOffer()); } @@ -1128,12 +1130,26 @@ private void maybeShowTakeOfferFromUnsignedAccountWarning(Offer offer) { } } - private void maybeShowZelleWarning(PaymentAccount paymentAccount) { + private void maybeShowZelleWarning(PaymentAccount paymentAccount, Offer offer) { if (paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.ZELLE_ID) && !zelleWarningDisplayed) { zelleWarningDisplayed = true; UserThread.runAfter(GUIUtil::showZelleWarning, 500, TimeUnit.MILLISECONDS); } + if (paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.ZELLE_ID) && + !zelleExtraInfoDisplayed && !offer.getExtraInfo().isEmpty()) { + zelleExtraInfoDisplayed = true; + UserThread.runAfter(() -> { + new GenericMessageWindow() + .preamble(Res.get("payment.tradingRestrictions")) + .instruction(offer.getExtraInfo()) + .actionButtonText(Res.get("shared.iConfirm")) + .closeButtonText(Res.get("shared.close")) + .width(Layout.INITIAL_WINDOW_WIDTH) + .onClose(() -> close(false)) + .show(); + }, 500, TimeUnit.MILLISECONDS); + } } private void maybeShowFasterPaymentsWarning(PaymentAccount paymentAccount) { @@ -1235,6 +1251,40 @@ private void maybeShowCashAppWarning(PaymentAccount paymentAccount, Offer offer) } } + private void maybeShowSepaWarning(PaymentAccount paymentAccount, Offer offer) { + if (paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.SEPA_ID) && + !sepaExtraInfoDisplayed && !offer.getExtraInfo().isEmpty()) { + sepaExtraInfoDisplayed = true; + UserThread.runAfter(() -> { + new GenericMessageWindow() + .preamble(Res.get("payment.tradingRestrictions")) + .instruction(offer.getExtraInfo()) + .actionButtonText(Res.get("shared.iConfirm")) + .closeButtonText(Res.get("shared.close")) + .width(Layout.INITIAL_WINDOW_WIDTH) + .onClose(() -> close(false)) + .show(); + }, 500, TimeUnit.MILLISECONDS); + } + } + + private void maybeShowSepaInstantWarning(PaymentAccount paymentAccount, Offer offer) { + if (paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.SEPA_INSTANT_ID) && + !sepaInstantExtraInfoDisplayed && !offer.getExtraInfo().isEmpty()) { + sepaInstantExtraInfoDisplayed = true; + UserThread.runAfter(() -> { + new GenericMessageWindow() + .preamble(Res.get("payment.tradingRestrictions")) + .instruction(offer.getExtraInfo()) + .actionButtonText(Res.get("shared.iConfirm")) + .closeButtonText(Res.get("shared.close")) + .width(Layout.INITIAL_WINDOW_WIDTH) + .onClose(() -> close(false)) + .show(); + }, 500, TimeUnit.MILLISECONDS); + } + } + private Tuple2<Label, VBox> getTradeInputBox(HBox amountValueBox, String promptText) { Label descriptionLabel = new AutoTooltipLabel(promptText); descriptionLabel.setId("input-description-label"); diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index b52274ae57..c828338371 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -901,6 +901,7 @@ message ChaseQuickPayAccountPayload { message ZelleAccountPayload { string holder_name = 1; string email_or_mobile_nr = 2; + string extra_info = 3; } message CountryBasedPaymentAccountPayload { @@ -1021,12 +1022,14 @@ message SepaAccountPayload { string iban = 2; string bic = 3; string email = 4 [deprecated = true]; + string extra_info = 5; } message SepaInstantAccountPayload { string holder_name = 1; string iban = 2; string bic = 3; + string extra_info = 4; } message CryptoCurrencyAccountPayload {