From c40938f438a3b4b4c68ef717db800d80aa49b1ad Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 17 Jun 2022 09:14:15 -0400 Subject: [PATCH] support MoneyGram form api --- .../core/api/model/PaymentAccountForm.java | 12 ++++- .../api/model/PaymentAccountFormField.java | 5 ++ .../main/java/bisq/core/locale/BankUtil.java | 21 ++++----- .../java/bisq/core/locale/CountryUtil.java | 4 ++ .../java/bisq/core/payment/F2FAccount.java | 2 +- .../bisq/core/payment/MoneyGramAccount.java | 23 ++++++++-- .../bisq/core/payment/PaymentAccount.java | 46 ++++++++++++------- .../core/payment/payload/PaymentMethod.java | 3 +- proto/src/main/proto/pb.proto | 7 ++- 9 files changed, 87 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java b/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java index 1c96a6f8..144baa26 100644 --- a/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java +++ b/core/src/main/java/bisq/core/api/model/PaymentAccountForm.java @@ -71,7 +71,8 @@ public final class PaymentAccountForm implements PersistablePayload { CLEAR_X_CHANGE, SWIFT, F2F, - STRIKE; + STRIKE, + MONEY_GRAM; public static PaymentAccountForm.FormId fromProto(protobuf.PaymentAccountForm.FormId formId) { return ProtoUtil.enumFromProto(PaymentAccountForm.FormId.class, formId.name()); @@ -108,6 +109,15 @@ public final class PaymentAccountForm implements PersistablePayload { return new PaymentAccountForm(FormId.fromProto(proto.getId()), fields); } + public String getValue(PaymentAccountFormField.FieldId fieldId) { + for (PaymentAccountFormField field : fields) { + if (field.getId() == fieldId) { + return field.getValue(); + } + } + throw new IllegalArgumentException("Form does not contain field " + fieldId); + } + /** * Get a structured form for the given payment method. */ diff --git a/core/src/main/java/bisq/core/api/model/PaymentAccountFormField.java b/core/src/main/java/bisq/core/api/model/PaymentAccountFormField.java index 9da56a96..f8141912 100644 --- a/core/src/main/java/bisq/core/api/model/PaymentAccountFormField.java +++ b/core/src/main/java/bisq/core/api/model/PaymentAccountFormField.java @@ -21,6 +21,7 @@ import bisq.common.proto.ProtoUtil; import bisq.common.proto.persistable.PersistablePayload; import bisq.core.locale.Country; import bisq.core.locale.TradeCurrency; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -109,6 +110,7 @@ public final class PaymentAccountFormField implements PersistablePayload { public enum Component { TEXT, + TEXTAREA, SELECT_ONE, SELECT_MULTIPLE; @@ -133,6 +135,7 @@ public final class PaymentAccountFormField implements PersistablePayload { private List supportedCountries; private List supportedSepaEuroCountries; private List supportedSepaNonEuroCountries; + private List requiredForCountries; public PaymentAccountFormField(FieldId id) { this.id = id; @@ -152,6 +155,7 @@ public final class PaymentAccountFormField implements PersistablePayload { Optional.ofNullable(supportedCountries).ifPresent(e -> builder.addAllSupportedCountries(ProtoUtil.collectionToProto(supportedCountries, protobuf.Country.class))); Optional.ofNullable(supportedSepaEuroCountries).ifPresent(e -> builder.addAllSupportedSepaEuroCountries(ProtoUtil.collectionToProto(supportedSepaEuroCountries, protobuf.Country.class))); Optional.ofNullable(supportedSepaNonEuroCountries).ifPresent(e -> builder.addAllSupportedSepaNonEuroCountries(ProtoUtil.collectionToProto(supportedSepaNonEuroCountries, protobuf.Country.class))); + Optional.ofNullable(requiredForCountries).ifPresent(builder::addAllRequiredForCountries); return builder.build(); } @@ -165,6 +169,7 @@ public final class PaymentAccountFormField implements PersistablePayload { formField.supportedCountries = proto.getSupportedCountriesList().isEmpty() ? null : proto.getSupportedCountriesList().stream().map(Country::fromProto).collect(Collectors.toList()); formField.supportedSepaEuroCountries = proto.getSupportedSepaEuroCountriesList().isEmpty() ? null : proto.getSupportedSepaEuroCountriesList().stream().map(Country::fromProto).collect(Collectors.toList()); formField.supportedSepaNonEuroCountries = proto.getSupportedSepaNonEuroCountriesList().isEmpty() ? null : proto.getSupportedSepaNonEuroCountriesList().stream().map(Country::fromProto).collect(Collectors.toList()); + formField.requiredForCountries = proto.getRequiredForCountriesList() == null ? null : new ArrayList(proto.getRequiredForCountriesList()); return formField; } } diff --git a/core/src/main/java/bisq/core/locale/BankUtil.java b/core/src/main/java/bisq/core/locale/BankUtil.java index ddc3d2cd..163ad4bc 100644 --- a/core/src/main/java/bisq/core/locale/BankUtil.java +++ b/core/src/main/java/bisq/core/locale/BankUtil.java @@ -20,7 +20,7 @@ package bisq.core.locale; import java.util.ArrayList; import java.util.Arrays; import java.util.List; - +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -264,18 +264,15 @@ public class BankUtil { } } + public static List getAllStateRequiredCountries() { + List codes = List.of("US", "CA", "AU", "MY", "MX", "CN"); + List list = CountryUtil.getCountries(codes); + list.sort((a, b) -> a.name.compareTo(b.name)); + return list; + } + public static boolean isStateRequired(String countryCode) { - switch (countryCode) { - case "US": - case "CA": - case "AU": - case "MY": - case "MX": - case "CN": - return true; - default: - return false; - } + return getAllStateRequiredCountries().stream().map(country -> country.code).collect(Collectors.toList()).contains(countryCode); } public static boolean isNationalAccountIdRequired(String countryCode) { diff --git a/core/src/main/java/bisq/core/locale/CountryUtil.java b/core/src/main/java/bisq/core/locale/CountryUtil.java index c1b5e381..7df50cd6 100644 --- a/core/src/main/java/bisq/core/locale/CountryUtil.java +++ b/core/src/main/java/bisq/core/locale/CountryUtil.java @@ -41,6 +41,10 @@ public class CountryUtil { return countries.stream().map(country -> country.code).collect(Collectors.toList()); } + public static Country getCountry(String code) { + return getCountries(List.of(code)).get(0); + } + public static List getCountries(List codes) { List countries = new ArrayList(); for (String code : codes) { diff --git a/core/src/main/java/bisq/core/payment/F2FAccount.java b/core/src/main/java/bisq/core/payment/F2FAccount.java index 78d80e1c..920f3291 100644 --- a/core/src/main/java/bisq/core/payment/F2FAccount.java +++ b/core/src/main/java/bisq/core/payment/F2FAccount.java @@ -84,7 +84,7 @@ public final class F2FAccount extends CountryBasedPaymentAccount { public String getExtraInfo() { return ((F2FAccountPayload) paymentAccountPayload).getExtraInfo(); } - + @Override protected PaymentAccountFormField getEmptyFormField(PaymentAccountFormField.FieldId fieldId) { var field = super.getEmptyFormField(fieldId); diff --git a/core/src/main/java/bisq/core/payment/MoneyGramAccount.java b/core/src/main/java/bisq/core/payment/MoneyGramAccount.java index 7f44dd98..30dff1ea 100644 --- a/core/src/main/java/bisq/core/payment/MoneyGramAccount.java +++ b/core/src/main/java/bisq/core/payment/MoneyGramAccount.java @@ -40,6 +40,16 @@ public final class MoneyGramAccount extends PaymentAccount { @Nullable private Country country; + private static final List INPUT_FIELD_IDS = List.of( + PaymentAccountFormField.FieldId.ACCOUNT_NAME, + PaymentAccountFormField.FieldId.COUNTRY, + PaymentAccountFormField.FieldId.STATE, + PaymentAccountFormField.FieldId.HOLDER_NAME, + PaymentAccountFormField.FieldId.EMAIL, + PaymentAccountFormField.FieldId.TRADE_CURRENCIES, + PaymentAccountFormField.FieldId.SALT + ); + public static final List SUPPORTED_CURRENCIES = List.of( new FiatCurrency("AED"), new FiatCurrency("ARS"), @@ -108,7 +118,7 @@ public final class MoneyGramAccount extends PaymentAccount { @Override public @NotNull List getInputFieldIds() { - throw new RuntimeException("Not implemented"); + return INPUT_FIELD_IDS; } @Nullable @@ -145,7 +155,14 @@ public final class MoneyGramAccount extends PaymentAccount { return ((MoneyGramAccountPayload) paymentAccountPayload).getState(); } - public void setState(String email) { - ((MoneyGramAccountPayload) paymentAccountPayload).setState(email); + public void setState(String state) { + ((MoneyGramAccountPayload) paymentAccountPayload).setState(state); + } + + @Override + protected PaymentAccountFormField getEmptyFormField(PaymentAccountFormField.FieldId fieldId) { + var field = super.getEmptyFormField(fieldId); + if (field.getId() == PaymentAccountFormField.FieldId.HOLDER_NAME) field.setLabel("Full name (first, middle, last)"); + return field; } } diff --git a/core/src/main/java/bisq/core/payment/PaymentAccount.java b/core/src/main/java/bisq/core/payment/PaymentAccount.java index 5ac61395..2d23702e 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccount.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccount.java @@ -19,6 +19,7 @@ package bisq.core.payment; import bisq.core.api.model.PaymentAccountForm; import bisq.core.api.model.PaymentAccountFormField; +import bisq.core.locale.BankUtil; import bisq.core.locale.Country; import bisq.core.locale.CountryUtil; import bisq.core.locale.CurrencyUtil; @@ -379,15 +380,15 @@ public abstract class PaymentAccount implements PersistablePayload { checkNotEmpty(value); break; case COUNTRY: - List supportedCountries = ((CountryBasedPaymentAccount) this).getSupportedCountries(); - if (supportedCountries == null || supportedCountries.isEmpty()) { - if (!CountryUtil.findCountryByCode(value).isPresent()) throw new IllegalArgumentException("Invalid country code: " + value); - } else { - System.out.println("BUT WE SUPPORT THESE COUNTRIES!"); - System.out.println(supportedCountries); - List supportedCountryCodes = CountryUtil.getCountryCodes(supportedCountries); - if (!supportedCountryCodes.contains(value)) throw new IllegalArgumentException("Country is not supported by " + getPaymentMethod().getId() + ": " + value); + if (this instanceof CountryBasedPaymentAccount) { + List supportedCountries = ((CountryBasedPaymentAccount) this).getSupportedCountries(); + if (supportedCountries != null && !supportedCountries.isEmpty()) { + List supportedCountryCodes = CountryUtil.getCountryCodes(supportedCountries); + if (!supportedCountryCodes.contains(value)) throw new IllegalArgumentException("Country is not supported by " + getPaymentMethod().getId() + ": " + value); + return; + } } + if (!CountryUtil.findCountryByCode(value).isPresent()) throw new IllegalArgumentException("Invalid country code: " + value); break; case EMAIL: checkNotEmpty(value); @@ -414,7 +415,7 @@ public abstract class PaymentAccount implements PersistablePayload { case IFSC: throw new IllegalArgumentException("Not implemented"); case INTERMEDIARY_COUNTRY_CODE: - if (!CountryUtil.findCountryByCode(value).isPresent()) throw new IllegalArgumentException("Invalid country code: " + value); // TODO: value must be within supported countries unless all countries supported + if (!CountryUtil.findCountryByCode(value).isPresent()) throw new IllegalArgumentException("Invalid country code: " + value); break; case MOBILE_NR: throw new IllegalArgumentException("Not implemented"); @@ -440,7 +441,17 @@ public abstract class PaymentAccount implements PersistablePayload { case SPECIAL_INSTRUCTIONS: break; case STATE: - throw new IllegalArgumentException("Not implemented"); + String countryCode = form.getValue(PaymentAccountFormField.FieldId.COUNTRY); + System.out.println("BACKEND RECEIVED STATE COUNTRY CODE: " + countryCode); + System.out.println("BACKEND RECEIVED STATE: " + value); + boolean isStateRequired = BankUtil.isStateRequired(countryCode); + System.out.println("IS STATE REQUIRED :" + isStateRequired); + if (value == null || value.isEmpty()) { + if (isStateRequired) throw new IllegalArgumentException("Must provide state for country " + countryCode); + } else { + if (!isStateRequired) throw new IllegalArgumentException("Must not provide state for country " + countryCode); + } + break; case TRADE_CURRENCIES: checkNotEmpty(value); List currencyCodes = commaDelimitedCodesToList.apply(value); @@ -497,7 +508,7 @@ public abstract class PaymentAccount implements PersistablePayload { case BANK_ACCOUNT_TYPE: throw new IllegalArgumentException("Not implemented"); case BANK_ADDRESS: - field.setComponent(PaymentAccountFormField.Component.TEXT); + field.setComponent(PaymentAccountFormField.Component.TEXTAREA); field.setLabel("Receiving Bank address"); break; case BANK_BRANCH: @@ -531,7 +542,7 @@ public abstract class PaymentAccount implements PersistablePayload { field.setLabel("Account No. (or IBAN)"); break; case BENEFICIARY_ADDRESS: - field.setComponent(PaymentAccountFormField.Component.TEXT); + field.setComponent(PaymentAccountFormField.Component.TEXTAREA); field.setLabel("Beneficiary address"); break; case BENEFICIARY_CITY: @@ -561,7 +572,7 @@ public abstract class PaymentAccount implements PersistablePayload { case COUNTRY: field.setComponent(PaymentAccountFormField.Component.SELECT_ONE); field.setLabel("Country"); - field.setSupportedCountries(((CountryBasedPaymentAccount) this).getSupportedCountries()); + if (this instanceof CountryBasedPaymentAccount) field.setSupportedCountries(((CountryBasedPaymentAccount) this).getSupportedCountries()); break; case EMAIL: field.setComponent(PaymentAccountFormField.Component.TEXT); @@ -573,7 +584,7 @@ public abstract class PaymentAccount implements PersistablePayload { field.setLabel("Email or mobile number"); break; case EXTRA_INFO: - field.setComponent(PaymentAccountFormField.Component.TEXT); + field.setComponent(PaymentAccountFormField.Component.TEXTAREA); field.setLabel("Optional additional information"); break; case HOLDER_ADDRESS: @@ -595,7 +606,7 @@ public abstract class PaymentAccount implements PersistablePayload { case IFSC: throw new IllegalArgumentException("Not implemented"); case INTERMEDIARY_ADDRESS: - field.setComponent(PaymentAccountFormField.Component.TEXT); + field.setComponent(PaymentAccountFormField.Component.TEXTAREA); field.setLabel("Intermediary Bank address"); break; case INTERMEDIARY_BRANCH: @@ -641,7 +652,10 @@ public abstract class PaymentAccount implements PersistablePayload { field.setLabel("Special instructions"); break; case STATE: - throw new IllegalArgumentException("Not implemented"); + field.setComponent(PaymentAccountFormField.Component.TEXT); + field.setLabel("State/Province/Region"); + field.setRequiredForCountries(CountryUtil.getCountryCodes(BankUtil.getAllStateRequiredCountries())); + break; case TRADE_CURRENCIES: field.setComponent(PaymentAccountFormField.Component.SELECT_MULTIPLE); field.setLabel("Supported currencies"); 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 86c7215b..69f03f67 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -334,7 +334,8 @@ public final class PaymentMethod implements PersistablePayload, Comparable paymentMethodIds.contains(paymentMethod.getId())).collect(Collectors.toList()); } diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index db07f176..e2b05706 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -2081,6 +2081,7 @@ message PaymentAccountForm { SWIFT = 4; F2F = 5; STRIKE = 6; + MONEY_GRAM = 7; } FormId id = 1; repeated PaymentAccountFormField fields = 2; @@ -2149,8 +2150,9 @@ message PaymentAccountFormField { } enum Component { TEXT = 0; - SELECT_ONE = 1; - SELECT_MULTIPLE = 2; + TEXTAREA = 1; + SELECT_ONE = 2; + SELECT_MULTIPLE = 3; } FieldId id = 1; Component component = 2; @@ -2163,4 +2165,5 @@ message PaymentAccountFormField { repeated Country supported_countries = 9; repeated Country supported_sepa_euro_countries = 10; repeated Country supported_sepa_non_euro_countries = 11; + repeated string required_for_countries = 12; } \ No newline at end of file