diff --git a/common/src/main/java/bisq/common/util/Utilities.java b/common/src/main/java/bisq/common/util/Utilities.java index 3d530b918d2..d0ef2304e35 100644 --- a/common/src/main/java/bisq/common/util/Utilities.java +++ b/common/src/main/java/bisq/common/util/Utilities.java @@ -591,4 +591,8 @@ public static String formatDurationAsWords(long durationMillis) { } return result; } + + public static String cleanString(String string) { + return string.replaceAll("[\\t\\n\\r]+", " "); + } } diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java b/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java index 345702623e6..f6c528a8ae5 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java @@ -92,6 +92,8 @@ public static PaymentAccount getPaymentAccount(PaymentMethod paymentMethod) { return new InstantCryptoCurrencyAccount(); case PaymentMethod.CAPITUAL_ID: return new CapitualAccount(); + case PaymentMethod.SWIFT_ID: + return new SwiftAccount(); // Cannot be deleted as it would break old trade history entries case PaymentMethod.OK_PAY_ID: diff --git a/core/src/main/java/bisq/core/payment/SwiftAccount.java b/core/src/main/java/bisq/core/payment/SwiftAccount.java new file mode 100644 index 00000000000..dd932622b4f --- /dev/null +++ b/core/src/main/java/bisq/core/payment/SwiftAccount.java @@ -0,0 +1,54 @@ +/* + * 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.CurrencyUtil; +import bisq.core.locale.FiatCurrency; +import bisq.core.locale.TradeCurrency; +import bisq.core.payment.payload.SwiftAccountPayload; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.payload.PaymentMethod; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +public final class SwiftAccount extends PaymentAccount { + public SwiftAccount() { + super(PaymentMethod.SWIFT); + } + + @Override + protected PaymentAccountPayload createPayload() { + return new SwiftAccountPayload(paymentMethod.getId(), id); + } + + public SwiftAccountPayload getPayload() { + return ((SwiftAccountPayload) this.paymentAccountPayload); + } + + public void selectAllTradeCurrencies() { + List currencyCodesSorted = CurrencyUtil.getAllSortedFiatCurrencies().stream() + .sorted(Comparator.comparing(TradeCurrency::getCode)) + .collect(Collectors.toList()); + tradeCurrencies.addAll(currencyCodesSorted); + } +} 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 dd6fe31db01..91721e06562 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -102,6 +102,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable. + */ + +package bisq.core.payment.payload; + +import bisq.core.locale.Res; + +import com.google.protobuf.Message; + +import java.nio.charset.StandardCharsets; + +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; + +@EqualsAndHashCode(callSuper = true) +@ToString +@Setter +@Getter +@Slf4j +public final class SwiftAccountPayload extends PaymentAccountPayload { + // payload data elements + private String bankSwiftCode = ""; + private String bankCountryCode = ""; + private String bankName = ""; + private String bankBranch = ""; + private String bankAddress = ""; + private String beneficiaryName = ""; + private String beneficiaryAccountNr = ""; + private String beneficiaryAddress = ""; + private String beneficiaryCity = ""; + private String beneficiaryPhone = ""; + private String specialInstructions = ""; + private String intermediarySwiftCode = ""; + private String intermediaryCountryCode = ""; + private String intermediaryName = ""; + private String intermediaryBranch = ""; + private String intermediaryAddress = ""; + + // constants + public static final String BANKPOSTFIX = ".bank"; + public static final String INTERMEDIARYPOSTFIX = ".intermediary"; + public static final String BENEFICIARYPOSTFIX = ".beneficiary"; + public static final String SWIFT_CODE = "payment.swift.swiftCode"; + public static final String COUNTRY = "payment.swift.country"; + public static final String SWIFT_ACCOUNT = "payment.swift.account"; + public static final String SNAME = "payment.swift.name"; + public static final String BRANCH = "payment.swift.branch"; + public static final String ADDRESS = "payment.swift.address"; + public static final String PHONE = "payment.swift.phone"; + + public SwiftAccountPayload(String paymentMethod, String id) { + super(paymentMethod, id); + } + + private SwiftAccountPayload(String paymentMethod, + String id, + String bankSwiftCode, + String bankCountryCode, + String bankName, + String bankBranch, + String bankAddress, + String beneficiaryName, + String beneficiaryAccountNr, + String beneficiaryAddress, + String beneficiaryCity, + String beneficiaryPhone, + String specialInstructions, + String intermediarySwiftCode, + String intermediaryCountryCode, + String intermediaryName, + String intermediaryBranch, + String intermediaryAddress, + long maxTradePeriod, + Map excludeFromJsonDataMap) { + super(paymentMethod, + id, + maxTradePeriod, + excludeFromJsonDataMap); + + this.bankSwiftCode = bankSwiftCode; + this.bankCountryCode = bankCountryCode; + this.bankName = bankName; + this.bankBranch = bankBranch; + this.bankAddress = bankAddress; + this.beneficiaryName = beneficiaryName; + this.beneficiaryAccountNr = beneficiaryAccountNr; + this.beneficiaryAddress = beneficiaryAddress; + this.beneficiaryCity = beneficiaryCity; + this.beneficiaryPhone = beneficiaryPhone; + this.specialInstructions = specialInstructions; + this.intermediarySwiftCode = intermediarySwiftCode; + this.intermediaryCountryCode = intermediaryCountryCode; + this.intermediaryName = intermediaryName; + this.intermediaryBranch = intermediaryBranch; + this.intermediaryAddress = intermediaryAddress; + } + + @Override + public Message toProtoMessage() { + return getPaymentAccountPayloadBuilder() + .setSwiftAccountPayload(protobuf.SwiftAccountPayload.newBuilder() + .setBankSwiftCode(bankSwiftCode) + .setBankCountryCode(bankCountryCode) + .setBankName(bankName) + .setBankBranch(bankBranch) + .setBankAddress(bankAddress) + .setBeneficiaryName(beneficiaryName) + .setBeneficiaryAccountNr(beneficiaryAccountNr) + .setBeneficiaryAddress(beneficiaryAddress) + .setBeneficiaryCity(beneficiaryCity) + .setBeneficiaryPhone(beneficiaryPhone) + .setSpecialInstructions(specialInstructions) + .setIntermediarySwiftCode(intermediarySwiftCode) + .setIntermediaryCountryCode(intermediaryCountryCode) + .setIntermediaryName(intermediaryName) + .setIntermediaryBranch(intermediaryBranch) + .setIntermediaryAddress(intermediaryAddress) + ) + .build(); + } + + public static SwiftAccountPayload fromProto(protobuf.PaymentAccountPayload proto) { + protobuf.SwiftAccountPayload x = proto.getSwiftAccountPayload(); + return new SwiftAccountPayload(proto.getPaymentMethodId(), + proto.getId(), + x.getBankSwiftCode(), + x.getBankCountryCode(), + x.getBankName(), + x.getBankBranch(), + x.getBankAddress(), + x.getBeneficiaryName(), + x.getBeneficiaryAccountNr(), + x.getBeneficiaryAddress(), + x.getBeneficiaryCity(), + x.getBeneficiaryPhone(), + x.getSpecialInstructions(), + x.getIntermediarySwiftCode(), + x.getIntermediaryCountryCode(), + x.getIntermediaryName(), + x.getIntermediaryBranch(), + x.getIntermediaryAddress(), + proto.getMaxTradePeriod(), + new HashMap<>(proto.getExcludeFromJsonDataMap())); + } + + @Override + public String getPaymentDetails() { + return Res.get(paymentMethodId) + " - " + beneficiaryName; + } + + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + + @Override + public byte[] getAgeWitnessInputData() { + return super.getAgeWitnessInputData(beneficiaryAccountNr.getBytes(StandardCharsets.UTF_8)); + } + + public boolean usesIntermediaryBank() { + return (intermediarySwiftCode != null && intermediarySwiftCode.length() > 0); + } +} diff --git a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java index d51503a15c8..2c70420c4e9 100644 --- a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java @@ -53,6 +53,7 @@ import bisq.core.payment.payload.SepaAccountPayload; import bisq.core.payment.payload.SepaInstantAccountPayload; import bisq.core.payment.payload.SpecificBanksAccountPayload; +import bisq.core.payment.payload.SwiftAccountPayload; import bisq.core.payment.payload.SwishAccountPayload; import bisq.core.payment.payload.TransferwiseAccountPayload; import bisq.core.payment.payload.USPostalMoneyOrderAccountPayload; @@ -168,6 +169,8 @@ public PaymentAccountPayload fromProto(protobuf.PaymentAccountPayload proto) { return InstantCryptoCurrencyPayload.fromProto(proto); case CAPITUAL_ACCOUNT_PAYLOAD: return CapitualAccountPayload.fromProto(proto); + case SWIFT_ACCOUNT_PAYLOAD: + return SwiftAccountPayload.fromProto(proto); // Cannot be deleted as it would break old trade history entries case O_K_PAY_ACCOUNT_PAYLOAD: diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java index fcc57d6f89d..6f381b2d6ed 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java @@ -155,7 +155,8 @@ private enum PaymentMethodMapper { CASH_BY_MAIL, CAPITUAL, PAYSERA, - PAXUM + PAXUM, + SWIFT } @Getter diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index f1452c309f0..0fedd67a81c 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -660,6 +660,9 @@ portfolio.pending.step2_buyer.refTextWarn=Important: when making the payment, le # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.fees=If your bank charges you any fees to make the transfer, you are responsible for paying those fees. # suppress inspection "TrailingSpacesInProperty" +portfolio.pending.step2_buyer.fees.swift=Make sure to use the SHA (shared fee model) to send the SWIFT payment. \ + See more details at [HYPERLINK:https://bisq.wiki/SWIFT#Use_the_correct_fee_option]. +# suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.altcoin=Please transfer from your external {0} wallet\n{1} to the BTC seller.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.cash=Please go to a bank and pay {0} to the BTC seller.\n\n @@ -3280,6 +3283,27 @@ payment.secret=Secret question payment.answer=Answer payment.wallet=Wallet ID payment.capitual.cap=CAP Code + +# suppress inspection "UnusedProperty" +payment.swift.headline=International SWIFT Wire Transfer +payment.swift.title.bank=Receiving Bank +payment.swift.title.intermediary=Intermediary Bank (click to expand) +payment.swift.country.bank=Receiving Bank Country +payment.swift.country.intermediary=Intermediary Bank Country +payment.swift.swiftCode.bank=Receiving Bank SWIFT Code +payment.swift.swiftCode.intermediary=Intermediary Bank SWIFT Code +payment.swift.name.bank=Receiving Bank name +payment.swift.name.intermediary=Intermediary Bank name +payment.swift.branch.bank=Receiving Bank branch +payment.swift.branch.intermediary=Intermediary Bank branch +payment.swift.address.bank=Receiving Bank address +payment.swift.address.intermediary=Intermediary Bank address +payment.swift.address.beneficiary=Beneficiary address +payment.swift.phone.beneficiary=Beneficiary phone number +payment.swift.account=Account No. (or IBAN) +payment.swift.use.intermediary=Use Intermediary Bank +payment.swift.showPaymentInfo=Show Payment Information... + payment.amazon.site=Buy giftcard at payment.ask=Ask in Trader Chat payment.uphold.accountId=Username or email or phone no. @@ -3411,6 +3435,28 @@ payment.account.amazonGiftCard.addCountryInfo={0}\n\ This will not affect your account age status. payment.amazonGiftCard.upgrade.headLine=Update Amazon Gift Card account +payment.swift.info=Carefully review the core guidelines for using SWIFT on Bisq:\n\ +\n\ +- fill all fields completely and accurately \n\ +- buyer must send payment in currency specified by the offer maker \n\ +- buyer must use the shared fee model (SHA) \n\ +- buyer and seller may incur fees, so they should check their bank's fee schedules beforehand \n\ +\n\ +SWIFT is more sophisticated than other payment methods, so please take a moment to review full guidance on the wiki [HYPERLINK:https://bisq.wiki/SWIFT]. + +payment.swift.info.buyer=To buy bitcoin with SWIFT, you must:\n\ +\n\ +- send payment in the currency specified by the offer maker \n\ +- use the shared fee model (SHA) to send payment\n\ +\n\ +Please review further guidance on the wiki to avoid penalties and ensure smooth trades [HYPERLINK:https://bisq.wiki/SWIFT]. + +payment.swift.info.seller=SWIFT senders are required to use the shared payment model (SHA) to send payments.\n\ +\n\ +If you receive a SWIFT payment that does not use SHA, open a mediation ticket.\n\ +\n\ +Please review further guidance on the wiki to avoid penalties and ensure smooth trades [HYPERLINK:https://bisq.wiki/SWIFT]. + payment.usPostalMoneyOrder.info=Trading using US Postal Money Orders (USPMO) on Bisq requires that you understand the following:\n\ \n\ - BTC buyers must write the BTC Seller’s name in both the Payer and the Payee’s fields & take a high-resolution photo of the USPMO and envelope with proof of tracking before sending.\n\ @@ -3581,6 +3627,8 @@ AMAZON_GIFT_CARD=Amazon eGift Card BLOCK_CHAINS_INSTANT=Altcoins Instant # suppress inspection "UnusedProperty" CAPITUAL=Capitual +# suppress inspection "UnusedProperty" +SWIFT=SWIFT International Wire Transfer # Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" @@ -3639,6 +3687,8 @@ AMAZON_GIFT_CARD_SHORT=Amazon eGift Card BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant # suppress inspection "UnusedProperty" CAPITUAL_SHORT=Capitual +# suppress inspection "UnusedProperty" +SWIFT_SHORT=SWIFT # Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/SwiftForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/SwiftForm.java new file mode 100644 index 00000000000..45d6d838f0a --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/SwiftForm.java @@ -0,0 +1,351 @@ +/* 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.AutoTooltipButton; +import bisq.desktop.components.AutoTooltipCheckBox; +import bisq.desktop.components.InputTextField; +import bisq.desktop.main.overlays.popups.Popup; +import bisq.desktop.main.overlays.windows.SwiftPaymentDetails; +import bisq.desktop.util.GUIUtil; +import bisq.desktop.util.Layout; +import bisq.desktop.util.validation.LengthValidator; + +import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.locale.Country; +import bisq.core.locale.CountryUtil; +import bisq.core.locale.Res; +import bisq.core.payment.PaymentAccount; +import bisq.core.payment.SwiftAccount; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.payload.SwiftAccountPayload; +import bisq.core.trade.Trade; +import bisq.core.util.coin.CoinFormatter; +import bisq.core.util.validation.InputValidator; + +import com.jfoenix.controls.JFXTextArea; + +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TextArea; +import javafx.scene.control.TitledPane; +import javafx.scene.layout.GridPane; + +import javafx.geometry.Insets; + +import java.util.function.Consumer; + +import static bisq.common.util.Utilities.cleanString; +import static bisq.core.payment.payload.SwiftAccountPayload.*; +import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField; +import static bisq.desktop.util.FormBuilder.addInputTextField; +import static bisq.desktop.util.FormBuilder.addTopLabelTextArea; +import static bisq.desktop.util.FormBuilder.addTopLabelTextField; + +public class SwiftForm extends PaymentMethodForm { + private final SwiftAccountPayload formData; + private final AutoTooltipCheckBox useIntermediaryCheck; + private final LengthValidator defaultValidator = new LengthValidator(2, 34); + private final LengthValidator swiftValidator = new LengthValidator(11, 11); + private final LengthValidator accountNrValidator = new LengthValidator(2, 40); + private final LengthValidator addressValidator = new LengthValidator(1, 100); + + public SwiftForm(PaymentAccount paymentAccount, + AccountAgeWitnessService accountAgeWitnessService, + InputValidator defaultValidator, GridPane gridPane, int gridRow, CoinFormatter formatter) { + super(paymentAccount, accountAgeWitnessService, defaultValidator, gridPane, gridRow, formatter); + this.formData = ((SwiftAccount) paymentAccount).getPayload(); + this.useIntermediaryCheck = new AutoTooltipCheckBox(Res.get("payment.swift.use.intermediary")); + } + + @Override + public void addFormForAddAccount() { + ((SwiftAccount) paymentAccount).selectAllTradeCurrencies(); + new Popup().information(Res.get("payment.swift.info")) + .width(900) + .closeButtonText(Res.get("shared.iUnderstand")) + .show(); + gridRowFrom = gridRow + 1; + addFieldsForBankEdit(true, this::setBankSwiftCode, this::setBankName, this::setBankBranch, this::setBankAddress); + addFieldsForBankEdit(false, this::setIntermediarySwiftCode, this::setIntermediaryName, this::setIntermediaryBranch, this::setIntermediaryAddress); + addFieldsForBeneficiaryEdit(); + addLimitations(false); + addAccountNameTextFieldWithAutoFillToggleButton(); + } + + @Override + protected void autoFillNameTextField() { + setAccountNameWithString(formData.getBeneficiaryName()); + } + + @Override + public void addFormForDisplayAccount() { + gridRowFrom = gridRow; + addTopLabelTextField(gridPane, gridRow, Res.get("payment.account.name"), paymentAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.paymentMethod"), Res.get(paymentAccount.getPaymentMethod().getId())); + + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(SWIFT_CODE + BANKPOSTFIX), formData.getBankSwiftCode()); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(COUNTRY + BANKPOSTFIX), CountryUtil.getNameAndCode(formData.getBankCountryCode())); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(SNAME + BANKPOSTFIX), formData.getBankName()); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(BRANCH + BANKPOSTFIX), formData.getBankBranch()); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(ADDRESS + BANKPOSTFIX), cleanString(formData.getBankAddress())); + + if (formData.usesIntermediaryBank()) { + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(SWIFT_CODE + INTERMEDIARYPOSTFIX), formData.getIntermediarySwiftCode(), Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(COUNTRY + INTERMEDIARYPOSTFIX), CountryUtil.getNameAndCode(formData.getIntermediaryCountryCode())); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(SNAME + INTERMEDIARYPOSTFIX), formData.getIntermediaryName()); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(BRANCH + INTERMEDIARYPOSTFIX), formData.getIntermediaryBranch()); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(ADDRESS + INTERMEDIARYPOSTFIX), cleanString(formData.getIntermediaryAddress())); + } + + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.account.owner"), formData.getBeneficiaryName(), Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(SWIFT_ACCOUNT), formData.getBeneficiaryAccountNr()); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(ADDRESS + BENEFICIARYPOSTFIX), cleanString(formData.getBeneficiaryAddress())); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get(PHONE + BENEFICIARYPOSTFIX), formData.getBeneficiaryPhone()); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.account.city"), formData.getBeneficiaryCity()); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.country"), CountryUtil.getNameAndCode(formData.getBankCountryCode())); // same as receiving bank country + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.shared.extraInfo"), cleanString(formData.getSpecialInstructions())); + + gridPane.add(new Label(""), 0, ++gridRow); // spacer + addLimitations(true); + } + + @Override + public void updateAllInputsValid() { + SwiftAccountPayload data = formData; + + // intermediary bank details are optional, but if specified must be valid + boolean intermediaryValidIfSpecified = !useIntermediaryCheck.isSelected() && !data.usesIntermediaryBank() || + data.usesIntermediaryBank() && (swiftValidator.validate(data.getIntermediarySwiftCode()).isValid + && defaultValidator.validate(data.getIntermediaryCountryCode()).isValid + && defaultValidator.validate(data.getIntermediaryName()).isValid + && defaultValidator.validate(data.getIntermediaryBranch()).isValid + && addressValidator.validate(data.getIntermediaryAddress()).isValid + ); + + allInputsValid.set(isAccountNameValid() + && swiftValidator.validate(data.getBankSwiftCode()).isValid + && defaultValidator.validate(data.getBankCountryCode()).isValid + && defaultValidator.validate(data.getBankName()).isValid + && defaultValidator.validate(data.getBankBranch()).isValid + && addressValidator.validate(data.getBankAddress()).isValid + && defaultValidator.validate(data.getBeneficiaryName()).isValid + && accountNrValidator.validate(data.getBeneficiaryAccountNr()).isValid + && addressValidator.validate(data.getBeneficiaryAddress()).isValid + && defaultValidator.validate(data.getBeneficiaryPhone()).isValid + && defaultValidator.validate(data.getBeneficiaryCity()).isValid + && paymentAccount.getTradeCurrencies().size() > 0 + && intermediaryValidIfSpecified); + } + + // Here we need to show information to buyer so they can make the fiat payment, however there is only enough space + // on the trade screen for ~4 fields. + // Since SWIFT has an unusually large number of fields, it will be better to offer a button which will show + // the SWIFT information in a popup screen. + public static int addFormForBuyer(GridPane gridPane, int gridRow, + PaymentAccountPayload paymentAccountPayload, Trade trade) { + SwiftAccountPayload swiftAccountPayload = (SwiftAccountPayload) paymentAccountPayload; + Button button = new AutoTooltipButton(Res.get("payment.swift.showPaymentInfo")); + GridPane.setRowIndex(button, gridRow); + GridPane.setColumnIndex(button, 1); + gridPane.getChildren().add(button); + GridPane.setMargin(button, new Insets(Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, Layout.FLOATING_LABEL_DISTANCE)); + button.setOnAction((e) -> new SwiftPaymentDetails(swiftAccountPayload, trade).show()); + return gridRow; + } + + private void addFieldsForBankEdit(boolean isPrimary, + Consumer onSwiftCodeSelected, + Consumer onNameSelected, + Consumer onBranchSelected, + Consumer onAddressSelected) { + GridPane gridPane2 = new GridPane(); + gridPane2.getColumnConstraints().add(gridPane.getColumnConstraints().get(0)); + TitledPane titledPane = new TitledPane(isPrimary ? Res.get("payment.swift.title" + BANKPOSTFIX) : Res.get("payment.swift.title" + INTERMEDIARYPOSTFIX), gridPane2); + titledPane.setExpanded(isPrimary); + gridPane.add(titledPane, 0, ++gridRow); + + int gridRow2 = 0; + if (!isPrimary) { + // secondary bank (optional) has a checkbox to specify if it is being used + gridPane2.add(useIntermediaryCheck, 0, ++gridRow2); + } + String label = isPrimary ? Res.get(SWIFT_CODE + BANKPOSTFIX) : Res.get(SWIFT_CODE + INTERMEDIARYPOSTFIX); + InputTextField bankSwiftCodeField = addInputTextField(gridPane2, ++gridRow2, label); + bankSwiftCodeField.setPromptText(label); + bankSwiftCodeField.setValidator(swiftValidator); + bankSwiftCodeField.textProperty().addListener((ov, oldValue, newValue) -> onSwiftCodeSelected.accept(newValue)); + + if (isPrimary) { + gridRow2 = GUIUtil.addRegionCountry(gridPane2, gridRow2, this::setBankCountry); + } else { + gridRow2 = GUIUtil.addRegionCountry(gridPane2, ++gridRow2, this::setIntermediaryCountry); + } + + label = isPrimary ? Res.get(SNAME + BANKPOSTFIX) : Res.get(SNAME + INTERMEDIARYPOSTFIX); + InputTextField bankNameField = addInputTextField(gridPane2, ++gridRow2, label); + bankNameField.setPromptText(label); + bankNameField.setValidator(defaultValidator); + bankNameField.textProperty().addListener((ov, oldValue, newValue) -> onNameSelected.accept(newValue)); + + label = isPrimary ? Res.get(BRANCH + BANKPOSTFIX) : Res.get(BRANCH + INTERMEDIARYPOSTFIX); + InputTextField bankBranchField = addInputTextField(gridPane2, ++gridRow2, label); + bankBranchField.setPromptText(label); + bankBranchField.setValidator(defaultValidator); + bankBranchField.textProperty().addListener((ov, oldValue, newValue) -> onBranchSelected.accept(newValue)); + + label = isPrimary ? Res.get(ADDRESS + BANKPOSTFIX) : Res.get(ADDRESS + INTERMEDIARYPOSTFIX); + TextArea bankAddressTextArea = addTopLabelTextArea(gridPane2, ++gridRow2, label, label).second; + bankAddressTextArea.setMinHeight(70); + bankAddressTextArea.textProperty().addListener((ov, oldValue, newValue) -> onAddressSelected.accept(newValue)); + + // intermediary bank can be enabled/disabled via checkbox + if (!isPrimary) { + useIntermediaryCheck.setOnAction((e) -> { + for (Node x : gridPane2.getChildren()) { + if (x == useIntermediaryCheck) + continue; + x.setDisable(!useIntermediaryCheck.isSelected()); + } + if (!useIntermediaryCheck.isSelected()) { + bankSwiftCodeField.setText(""); + bankNameField.setText(""); + bankBranchField.setText(""); + bankAddressTextArea.setText(""); + } + updateFromInputs(); + }); + // make the intermediary fields initially greyed out + for (Node x : gridPane2.getChildren()) { + if (x == useIntermediaryCheck) + continue; + x.setDisable(!useIntermediaryCheck.isSelected()); + } + } + } + + private void addFieldsForBeneficiaryEdit() { + String label = Res.get("payment.account.owner"); + InputTextField beneficiaryNameField = addInputTextField(gridPane, ++gridRow, label); + beneficiaryNameField.setPromptText(label); + beneficiaryNameField.setValidator(defaultValidator); + beneficiaryNameField.textProperty().addListener((ov, oldValue, newValue) -> { + formData.setBeneficiaryName(newValue.trim()); + updateFromInputs(); + }); + + label = Res.get(SWIFT_ACCOUNT); + InputTextField beneficiaryAccountNrField = addInputTextField(gridPane, ++gridRow, label); + beneficiaryAccountNrField.setPromptText(label); + beneficiaryAccountNrField.setValidator(defaultValidator); + beneficiaryAccountNrField.setValidator(accountNrValidator); + beneficiaryAccountNrField.textProperty().addListener((ov, oldValue, newValue) -> { + formData.setBeneficiaryAccountNr(newValue.trim()); + updateFromInputs(); + }); + + label = Res.get(ADDRESS + BENEFICIARYPOSTFIX); + TextArea beneficiaryAddressTextArea = addTopLabelTextArea(gridPane, ++gridRow, label, label).second; + beneficiaryAddressTextArea.setMinHeight(70); + beneficiaryAddressTextArea.textProperty().addListener((ov, oldValue, newValue) -> { + formData.setBeneficiaryAddress(newValue.trim()); + updateFromInputs(); + }); + + label = Res.get("payment.account.city"); + InputTextField beneficiaryCityField = addInputTextField(gridPane, ++gridRow, label); + beneficiaryCityField.setPromptText(label); + beneficiaryCityField.setValidator(defaultValidator); + beneficiaryCityField.textProperty().addListener((ov, oldValue, newValue) -> { + formData.setBeneficiaryCity(newValue.trim()); + updateFromInputs(); + }); + + label = Res.get(PHONE + BENEFICIARYPOSTFIX); + InputTextField beneficiaryPhoneField = addInputTextField(gridPane, ++gridRow, label); + beneficiaryPhoneField.setPromptText(label); + beneficiaryPhoneField.setValidator(defaultValidator); + beneficiaryPhoneField.textProperty().addListener((ov, oldValue, newValue) -> { + formData.setBeneficiaryPhone(newValue.trim()); + updateFromInputs(); + }); + + label = Res.get("payment.shared.optionalExtra"); + TextArea extraTextArea = addTopLabelTextArea(gridPane, ++gridRow, label, label).second; + extraTextArea.setMinHeight(70); + ((JFXTextArea) extraTextArea).setLabelFloat(false); + extraTextArea.textProperty().addListener((ov, oldValue, newValue) -> { + formData.setSpecialInstructions(newValue.trim()); + updateFromInputs(); + }); + } + + private void setBankSwiftCode(String value) { + formData.setBankSwiftCode(value.trim()); + updateFromInputs(); + } + + private void setBankName(String value) { + formData.setBankName(value.trim()); + updateFromInputs(); + } + + private void setBankBranch(String value) { + formData.setBankBranch(value.trim()); + updateFromInputs(); + } + + private void setBankAddress(String value) { + formData.setBankAddress(value.trim()); + updateFromInputs(); + } + + private void setIntermediarySwiftCode(String value) { + formData.setIntermediarySwiftCode(value.trim()); + updateFromInputs(); + } + + private void setIntermediaryName(String value) { + formData.setIntermediaryName(value.trim()); + updateFromInputs(); + } + + private void setIntermediaryBranch(String value) { + formData.setIntermediaryBranch(value.trim()); + updateFromInputs(); + } + + private void setIntermediaryAddress(String value) { + formData.setIntermediaryAddress(value.trim()); + updateFromInputs(); + } + + private void setBankCountry(Country country) { + if (country == null) + return; + formData.setBankCountryCode(country.code); + updateFromInputs(); + } + + private void setIntermediaryCountry(Country country) { + if (country == null) + return; + formData.setIntermediaryCountryCode(country.code); + updateFromInputs(); + } +} 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 d94b4b18728..09e3cb37b22 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 @@ -45,6 +45,7 @@ import bisq.desktop.components.paymentmethods.SepaForm; import bisq.desktop.components.paymentmethods.SepaInstantForm; import bisq.desktop.components.paymentmethods.SpecificBankForm; +import bisq.desktop.components.paymentmethods.SwiftForm; import bisq.desktop.components.paymentmethods.SwishForm; import bisq.desktop.components.paymentmethods.TransferwiseForm; import bisq.desktop.components.paymentmethods.PayseraForm; @@ -543,6 +544,8 @@ private PaymentMethodForm getPaymentMethodForm(PaymentMethod paymentMethod, Paym return new AmazonGiftCardForm(paymentAccount, accountAgeWitnessService, inputValidator, root, gridRow, formatter); case PaymentMethod.CAPITUAL_ID: return new CapitualForm(paymentAccount, accountAgeWitnessService, capitualValidator, inputValidator, root, gridRow, formatter); + case PaymentMethod.SWIFT_ID: + return new SwiftForm(paymentAccount, accountAgeWitnessService, inputValidator, root, gridRow, formatter); default: log.error("Not supported PaymentMethod: " + paymentMethod); return null; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java index 3794dc1b63f..5ac9a51b264 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java @@ -168,7 +168,7 @@ public abstract class MutableOfferView> exten protected int gridRow = 0; private final List editOfferElements = new ArrayList<>(); - private boolean clearXchangeWarningDisplayed, fasterPaymentsWarningDisplayed, isActivated; + private boolean clearXchangeWarningDisplayed, fasterPaymentsWarningDisplayed, swiftWarningDisplayed, isActivated; private InfoInputTextField marketBasedPriceInfoInputTextField, volumeInfoInputTextField, buyerSecurityDepositInfoInputTextField, triggerPriceInfoInputTextField; private AutoTooltipSlideToggleButton tradeFeeInBtcToggle, tradeFeeInBsqToggle; @@ -499,6 +499,19 @@ private void maybeShowFasterPaymentsWarning(PaymentAccount paymentAccount) { } } + private void maybeShowSwiftWarning(PaymentAccount paymentAccount) { + if (paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.SWIFT_ID) && !swiftWarningDisplayed) { + swiftWarningDisplayed = true; + UserThread.runAfter(() -> { + if (model.getDataModel().isBuyOffer()) { + GUIUtil.showSwiftWarningToBuyer(); + } else { + GUIUtil.showSwiftWarningToSeller(); + } + }, 500, TimeUnit.MILLISECONDS); + } + } + protected void onPaymentAccountsComboBoxSelected() { // Temporary deactivate handler as the payment account change can populate a new currency list and causes // unwanted selection events (item 0) @@ -508,6 +521,7 @@ protected void onPaymentAccountsComboBoxSelected() { if (paymentAccount != null) { maybeShowClearXchangeWarning(paymentAccount); maybeShowFasterPaymentsWarning(paymentAccount); + maybeShowSwiftWarning(paymentAccount); currencySelection.setVisible(paymentAccount.hasMultipleCurrencies()); currencySelection.setManaged(paymentAccount.hasMultipleCurrencies()); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java index 1d43cdd65ed..c8af97a49ea 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java @@ -163,7 +163,7 @@ public class TakeOfferView extends ActivatableViewAndModel amountFocusedListener, getShowWalletFundedNotificationListener; @@ -309,6 +309,7 @@ protected void activate() { maybeShowTakeOfferFromUnsignedAccountWarning(model.dataModel.getOffer()); maybeShowClearXchangeWarning(lastPaymentAccount); maybeShowFasterPaymentsWarning(lastPaymentAccount); + maybeShowSwiftWarning(lastPaymentAccount); maybeShowCashByMailWarning(lastPaymentAccount, model.dataModel.getOffer()); if (!DevEnv.isDaoActivated() && !model.isRange()) { @@ -831,6 +832,7 @@ private void addPaymentGroup() { if (paymentAccount != null) { maybeShowClearXchangeWarning(paymentAccount); maybeShowFasterPaymentsWarning(paymentAccount); + maybeShowSwiftWarning(paymentAccount); } model.onPaymentAccountSelected(paymentAccount); }); @@ -1272,6 +1274,19 @@ private void maybeShowFasterPaymentsWarning(PaymentAccount paymentAccount) { } } + private void maybeShowSwiftWarning(PaymentAccount paymentAccount) { + if (paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.SWIFT_ID) && !swiftWarningDisplayed) { + swiftWarningDisplayed = true; + UserThread.runAfter(() -> { + if (model.getOffer().getDirection() == OfferPayload.Direction.BUY) { + GUIUtil.showSwiftWarningToSeller(); // taking an offer to buy, we are the seller + } else { + GUIUtil.showSwiftWarningToBuyer(); // taking an offer to sell, we are the buyer + } + }, 500, TimeUnit.MILLISECONDS); + } + } + private void maybeShowCashByMailWarning(PaymentAccount paymentAccount, Offer offer) { if (paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.CASH_BY_MAIL_ID) && !cashByMailWarningDisplayed && !offer.getExtraInfo().isEmpty()) { diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SwiftPaymentDetails.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SwiftPaymentDetails.java new file mode 100644 index 00000000000..18556ad9534 --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SwiftPaymentDetails.java @@ -0,0 +1,114 @@ +/* + * 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.main.overlays.windows; + +import bisq.desktop.main.overlays.Overlay; +import bisq.desktop.util.DisplayUtils; + +import bisq.core.locale.CountryUtil; +import bisq.core.locale.Res; +import bisq.core.payment.payload.SwiftAccountPayload; +import bisq.core.trade.Trade; + +import javafx.scene.control.Label; +import javafx.geometry.Insets; + +import java.util.ArrayList; +import java.util.List; + +import static bisq.common.util.Utilities.cleanString; +import static bisq.common.util.Utilities.copyToClipboard; +import static bisq.core.payment.payload.SwiftAccountPayload.*; +import static bisq.desktop.util.FormBuilder.addConfirmationLabelLabel; +import static bisq.desktop.util.FormBuilder.addTitledGroupBg; + +public class SwiftPaymentDetails extends Overlay { + private final SwiftAccountPayload payload; + private final Trade trade; + private final List copyToClipboardData = new ArrayList<>(); + + public SwiftPaymentDetails(SwiftAccountPayload swiftAccountPayload, Trade trade) { + this.payload = swiftAccountPayload; + this.trade = trade; + } + + public void show() { + rowIndex = -1; + width = 918; + createGridPane(); + addContent(); + addButtons(); + display(); + } + + @Override + protected void cleanup() { + } + + @Override + protected void createGridPane() { + super.createGridPane(); + gridPane.setPadding(new Insets(35, 40, 30, 40)); + gridPane.getStyleClass().add("grid-pane"); + } + + private void addContent() { + int rows = payload.usesIntermediaryBank() ? 22 : 16; + addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("payment.swift.headline")); + + gridPane.add(new Label(""), 0, ++rowIndex); // spacer + addLabelsAndCopy(Res.get("portfolio.pending.step2_buyer.amountToTransfer"), DisplayUtils.formatVolumeWithCode(trade.getTradeVolume())); + addLabelsAndCopy(Res.get(SWIFT_CODE + BANKPOSTFIX), payload.getBankSwiftCode()); + addLabelsAndCopy(Res.get(SNAME + BANKPOSTFIX), payload.getBankName()); + addLabelsAndCopy(Res.get(BRANCH + BANKPOSTFIX), payload.getBankBranch()); + addLabelsAndCopy(Res.get(ADDRESS + BANKPOSTFIX), cleanString(payload.getBankAddress())); + addLabelsAndCopy(Res.get(COUNTRY + BANKPOSTFIX), CountryUtil.getNameAndCode(payload.getBankCountryCode())); + + if (payload.usesIntermediaryBank()) { + gridPane.add(new Label(""), 0, ++rowIndex); // spacer + addLabelsAndCopy(Res.get(SWIFT_CODE + INTERMEDIARYPOSTFIX), payload.getIntermediarySwiftCode()); + addLabelsAndCopy(Res.get(SNAME + INTERMEDIARYPOSTFIX), payload.getIntermediaryName()); + addLabelsAndCopy(Res.get(BRANCH + INTERMEDIARYPOSTFIX), payload.getIntermediaryBranch()); + addLabelsAndCopy(Res.get(ADDRESS + INTERMEDIARYPOSTFIX), cleanString(payload.getIntermediaryAddress())); + addLabelsAndCopy(Res.get(COUNTRY + INTERMEDIARYPOSTFIX), CountryUtil.getNameAndCode(payload.getIntermediaryCountryCode())); + } + + gridPane.add(new Label(""), 0, ++rowIndex); // spacer + addLabelsAndCopy(Res.get("payment.account.owner"), payload.getBeneficiaryName()); + addLabelsAndCopy(Res.get(SWIFT_ACCOUNT), payload.getBeneficiaryAccountNr()); + addLabelsAndCopy(Res.get(ADDRESS + BENEFICIARYPOSTFIX), cleanString(payload.getBeneficiaryAddress())); + addLabelsAndCopy(Res.get(PHONE + BENEFICIARYPOSTFIX), payload.getBeneficiaryPhone()); + addLabelsAndCopy(Res.get("payment.account.city"), payload.getBeneficiaryCity()); + addLabelsAndCopy(Res.get("payment.country"), CountryUtil.getNameAndCode(payload.getBankCountryCode())); + addLabelsAndCopy(Res.get("payment.shared.extraInfo"), cleanString(payload.getSpecialInstructions())); + + actionButtonText(Res.get("shared.copyToClipboard")); + onAction(() -> { + StringBuilder work = new StringBuilder(); + for (String s : copyToClipboardData) { + work.append(s).append(System.lineSeparator()); + } + copyToClipboard(work.toString()); + }); + } + + private void addLabelsAndCopy(String title, String value) { + addConfirmationLabelLabel(gridPane, ++rowIndex, title, value); + copyToClipboardData.add(title + " : " + value); + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java index 167b9140f6f..278de94f84a 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java @@ -48,6 +48,7 @@ import bisq.desktop.components.paymentmethods.SepaForm; import bisq.desktop.components.paymentmethods.SepaInstantForm; import bisq.desktop.components.paymentmethods.SpecificBankForm; +import bisq.desktop.components.paymentmethods.SwiftForm; import bisq.desktop.components.paymentmethods.SwishForm; import bisq.desktop.components.paymentmethods.TransferwiseForm; import bisq.desktop.components.paymentmethods.USPostalMoneyOrderForm; @@ -81,6 +82,7 @@ import bisq.core.payment.payload.MoneyGramAccountPayload; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.payment.payload.PaymentMethod; +import bisq.core.payment.payload.SwiftAccountPayload; import bisq.core.payment.payload.USPostalMoneyOrderAccountPayload; import bisq.core.payment.payload.WesternUnionAccountPayload; import bisq.core.trade.Trade; @@ -335,6 +337,9 @@ protected void addContent() { case PaymentMethod.CAPITUAL_ID: gridRow = CapitualForm.addFormForBuyer(gridPane, gridRow, paymentAccountPayload); break; + case PaymentMethod.SWIFT_ID: + gridRow = SwiftForm.addFormForBuyer(gridPane, gridRow, paymentAccountPayload, trade); + break; default: log.error("Not supported PaymentMethod: " + paymentMethodId); } @@ -604,6 +609,10 @@ private void showPopup() { } else if (paymentAccountPayload instanceof CashByMailAccountPayload || paymentAccountPayload instanceof HalCashAccountPayload) { message += Res.get("portfolio.pending.step2_buyer.pay", amount); + } else if (paymentAccountPayload instanceof SwiftAccountPayload) { + message += Res.get("portfolio.pending.step2_buyer.pay", amount) + + refTextWarn + "\n\n" + + Res.get("portfolio.pending.step2_buyer.fees.swift"); } else { message += Res.get("portfolio.pending.step2_buyer.pay", amount) + refTextWarn + "\n\n" + diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index 6160a860cae..f5177b3a154 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -778,6 +778,24 @@ public static void showFasterPaymentsWarning(Navigation navigation) { .show(); } + public static void showSwiftWarningToBuyer() { + String key = "confirmSwiftRequirementsBuyer"; + new Popup().information(Res.get("payment.swift.info.buyer")) + .width(900) + .closeButtonText(Res.get("shared.iConfirm")) + .dontShowAgainId(key) + .show(); + } + + public static void showSwiftWarningToSeller() { + String key = "confirmSwiftRequirementsSeller"; + new Popup().information(Res.get("payment.swift.info.seller")) + .width(900) + .closeButtonText(Res.get("shared.iConfirm")) + .dontShowAgainId(key) + .show(); + } + public static String getBitcoinURI(String address, Coin amount, String label) { return address != null ? BitcoinURI.convertToBitcoinURI(Address.fromString(Config.baseCurrencyNetworkParameters(), diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 0b19e1c7e31..b268b65c8c5 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -999,6 +999,7 @@ message PaymentAccountPayload { CapitualAccountPayload capitual_account_payload = 33; PayseraAccountPayload Paysera_account_payload = 34; PaxumAccountPayload Paxum_account_payload = 35; + SwiftAccountPayload swift_account_payload = 36; } map exclude_from_json_data = 15; } @@ -1232,6 +1233,27 @@ message CapitualAccountPayload { string account_nr = 1; } +message SwiftAccountPayload { + string beneficiary_name = 1; + string beneficiary_account_nr = 2; + string beneficiary_address = 3; + string beneficiary_city = 4; + string beneficiary_phone = 5; + string special_instructions = 6; + + string bank_swift_code = 7; + string bank_country_code = 8; + string bank_name = 9; + string bank_branch = 10; + string bank_address = 11; + + string intermediary_swift_code = 12; + string intermediary_country_code = 13; + string intermediary_name = 14; + string intermediary_branch = 15; + string intermediary_address = 16; +} + /////////////////////////////////////////////////////////////////////////////////////////// // PersistableEnvelope ///////////////////////////////////////////////////////////////////////////////////////////