This commit is contained in:
woodser 2025-06-21 18:49:29 +00:00 committed by GitHub
commit 38614d1470
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 238 additions and 50 deletions

View file

@ -36,6 +36,8 @@ import haveno.core.payment.InstantCryptoCurrencyAccount;
import haveno.core.payment.PaymentAccount; import haveno.core.payment.PaymentAccount;
import haveno.core.payment.PaymentAccountFactory; import haveno.core.payment.PaymentAccountFactory;
import haveno.core.payment.payload.PaymentMethod; import haveno.core.payment.payload.PaymentMethod;
import haveno.core.payment.validation.InteracETransferValidator;
import haveno.core.trade.HavenoUtils;
import haveno.core.user.User; import haveno.core.user.User;
import java.io.File; import java.io.File;
import static java.lang.String.format; import static java.lang.String.format;
@ -48,19 +50,24 @@ import lombok.extern.slf4j.Slf4j;
@Singleton @Singleton
@Slf4j @Slf4j
class CorePaymentAccountsService { public class CorePaymentAccountsService {
private final CoreAccountService accountService; private final CoreAccountService accountService;
private final AccountAgeWitnessService accountAgeWitnessService; private final AccountAgeWitnessService accountAgeWitnessService;
private final User user; private final User user;
public final InteracETransferValidator interacETransferValidator;
@Inject @Inject
public CorePaymentAccountsService(CoreAccountService accountService, public CorePaymentAccountsService(CoreAccountService accountService,
AccountAgeWitnessService accountAgeWitnessService, AccountAgeWitnessService accountAgeWitnessService,
User user) { User user,
InteracETransferValidator interacETransferValidator) {
this.accountService = accountService; this.accountService = accountService;
this.accountAgeWitnessService = accountAgeWitnessService; this.accountAgeWitnessService = accountAgeWitnessService;
this.user = user; this.user = user;
this.interacETransferValidator = interacETransferValidator;
HavenoUtils.corePaymentAccountService = this;
} }
PaymentAccount createPaymentAccount(PaymentAccountForm form) { PaymentAccount createPaymentAccount(PaymentAccountForm form) {

View file

@ -78,7 +78,15 @@ public final class PaymentAccountForm implements PersistablePayload {
CASH_APP, CASH_APP,
PAYPAL, PAYPAL,
VENMO, VENMO,
PAYSAFE; PAYSAFE,
WECHAT_PAY,
ALI_PAY,
SWISH,
TRANSFERWISE_USD,
AMAZON_GIFT_CARD,
ACH_TRANSFER,
INTERAC_E_TRANSFER,
US_POSTAL_MONEY_ORDER;
public static PaymentAccountForm.FormId fromProto(protobuf.PaymentAccountForm.FormId formId) { public static PaymentAccountForm.FormId fromProto(protobuf.PaymentAccountForm.FormId formId) {
return ProtoUtil.enumFromProto(PaymentAccountForm.FormId.class, formId.name()); return ProtoUtil.enumFromProto(PaymentAccountForm.FormId.class, formId.name());

View file

@ -19,6 +19,7 @@ package haveno.core.payment;
import haveno.core.api.model.PaymentAccountFormField; import haveno.core.api.model.PaymentAccountFormField;
import haveno.core.locale.TraditionalCurrency; import haveno.core.locale.TraditionalCurrency;
import haveno.core.locale.BankUtil;
import haveno.core.locale.TradeCurrency; import haveno.core.locale.TradeCurrency;
import haveno.core.payment.payload.AchTransferAccountPayload; import haveno.core.payment.payload.AchTransferAccountPayload;
import haveno.core.payment.payload.BankAccountPayload; import haveno.core.payment.payload.BankAccountPayload;
@ -34,6 +35,19 @@ public final class AchTransferAccount extends CountryBasedPaymentAccount impleme
public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("USD")); public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("USD"));
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.HOLDER_NAME,
PaymentAccountFormField.FieldId.HOLDER_ADDRESS,
PaymentAccountFormField.FieldId.BANK_NAME,
PaymentAccountFormField.FieldId.BRANCH_ID,
PaymentAccountFormField.FieldId.ACCOUNT_NR,
PaymentAccountFormField.FieldId.ACCOUNT_TYPE,
PaymentAccountFormField.FieldId.COUNTRY,
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.SALT
);
public AchTransferAccount() { public AchTransferAccount() {
super(PaymentMethod.ACH_TRANSFER); super(PaymentMethod.ACH_TRANSFER);
} }
@ -79,6 +93,15 @@ public final class AchTransferAccount extends CountryBasedPaymentAccount impleme
@Override @Override
public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() { public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented"); return INPUT_FIELD_IDS;
}
@Override
protected PaymentAccountFormField getEmptyFormField(PaymentAccountFormField.FieldId fieldId) {
var field = super.getEmptyFormField(fieldId);
if (field.getId() == PaymentAccountFormField.FieldId.TRADE_CURRENCIES) field.setComponent(PaymentAccountFormField.Component.SELECT_ONE);
if (field.getId() == PaymentAccountFormField.FieldId.BRANCH_ID) field.setLabel(BankUtil.getBranchIdLabel("US"));
if (field.getId() == PaymentAccountFormField.FieldId.ACCOUNT_TYPE) field.setLabel(BankUtil.getAccountTypeLabel("US"));
return field;
} }
} }

View file

@ -60,6 +60,13 @@ public final class AliPayAccount extends PaymentAccount {
new TraditionalCurrency("ZAR") new TraditionalCurrency("ZAR")
); );
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.ACCOUNT_NR,
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
PaymentAccountFormField.FieldId.SALT
);
public AliPayAccount() { public AliPayAccount() {
super(PaymentMethod.ALI_PAY); super(PaymentMethod.ALI_PAY);
setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0)); setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0));
@ -77,7 +84,7 @@ public final class AliPayAccount extends PaymentAccount {
@Override @Override
public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() { public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented"); return INPUT_FIELD_IDS;
} }
public void setAccountNr(String accountNr) { public void setAccountNr(String accountNr) {

View file

@ -46,6 +46,14 @@ public final class AmazonGiftCardAccount extends PaymentAccount {
new TraditionalCurrency("USD") new TraditionalCurrency("USD")
); );
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR,
PaymentAccountFormField.FieldId.COUNTRY,
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.SALT
);
@Nullable @Nullable
private Country country; private Country country;
@ -65,7 +73,7 @@ public final class AmazonGiftCardAccount extends PaymentAccount {
@Override @Override
public @NotNull List<PaymentAccountFormField.FieldId> getInputFieldIds() { public @NotNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented"); return INPUT_FIELD_IDS;
} }
public String getEmailOrMobileNr() { public String getEmailOrMobileNr() {
@ -97,4 +105,11 @@ public final class AmazonGiftCardAccount extends PaymentAccount {
private AmazonGiftCardAccountPayload getAmazonGiftCardAccountPayload() { private AmazonGiftCardAccountPayload getAmazonGiftCardAccountPayload() {
return (AmazonGiftCardAccountPayload) paymentAccountPayload; return (AmazonGiftCardAccountPayload) paymentAccountPayload;
} }
@Override
protected PaymentAccountFormField getEmptyFormField(PaymentAccountFormField.FieldId fieldId) {
var field = super.getEmptyFormField(fieldId);
if (field.getId() == PaymentAccountFormField.FieldId.TRADE_CURRENCIES) field.setComponent(PaymentAccountFormField.Component.SELECT_ONE);
return field;
}
} }

View file

@ -17,12 +17,15 @@
package haveno.core.payment; package haveno.core.payment;
import haveno.core.api.model.PaymentAccountForm;
import haveno.core.api.model.PaymentAccountFormField; import haveno.core.api.model.PaymentAccountFormField;
import haveno.core.locale.TraditionalCurrency; import haveno.core.locale.TraditionalCurrency;
import haveno.core.locale.TradeCurrency; import haveno.core.locale.TradeCurrency;
import haveno.core.payment.payload.InteracETransferAccountPayload; import haveno.core.payment.payload.InteracETransferAccountPayload;
import haveno.core.payment.payload.PaymentAccountPayload; import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.payment.payload.PaymentMethod; import haveno.core.payment.payload.PaymentMethod;
import haveno.core.payment.validation.InteracETransferValidator;
import haveno.core.trade.HavenoUtils;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -33,9 +36,22 @@ public final class InteracETransferAccount extends PaymentAccount {
public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("CAD")); public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("CAD"));
private final InteracETransferValidator interacETransferValidator;
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.HOLDER_NAME,
PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR,
PaymentAccountFormField.FieldId.QUESTION,
PaymentAccountFormField.FieldId.ANSWER,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.SALT
);
public InteracETransferAccount() { public InteracETransferAccount() {
super(PaymentMethod.INTERAC_E_TRANSFER); super(PaymentMethod.INTERAC_E_TRANSFER);
setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0)); setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0));
this.interacETransferValidator = HavenoUtils.corePaymentAccountService.interacETransferValidator;
if (interacETransferValidator == null) throw new IllegalArgumentException("InteracETransferValidator cannot be null");
} }
@Override @Override
@ -50,15 +66,15 @@ public final class InteracETransferAccount extends PaymentAccount {
@Override @Override
public @NotNull List<PaymentAccountFormField.FieldId> getInputFieldIds() { public @NotNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented"); return INPUT_FIELD_IDS;
} }
public void setEmail(String email) { public void setEmail(String email) {
((InteracETransferAccountPayload) paymentAccountPayload).setEmail(email); ((InteracETransferAccountPayload) paymentAccountPayload).setEmailOrMobileNr(email);
} }
public String getEmail() { public String getEmail() {
return ((InteracETransferAccountPayload) paymentAccountPayload).getEmail(); return ((InteracETransferAccountPayload) paymentAccountPayload).getEmailOrMobileNr();
} }
public void setAnswer(String answer) { public void setAnswer(String answer) {
@ -84,4 +100,18 @@ public final class InteracETransferAccount extends PaymentAccount {
public String getHolderName() { public String getHolderName() {
return ((InteracETransferAccountPayload) paymentAccountPayload).getHolderName(); return ((InteracETransferAccountPayload) paymentAccountPayload).getHolderName();
} }
public void validateFormField(PaymentAccountForm form, PaymentAccountFormField.FieldId fieldId, String value) {
switch (fieldId) {
case QUESTION:
processValidationResult(interacETransferValidator.questionValidator.validate(value));
break;
case ANSWER:
processValidationResult(interacETransferValidator.answerValidator.validate(value));
break;
default:
super.validateFormField(form, fieldId, value);
}
}
} }

View file

@ -435,7 +435,8 @@ public abstract class PaymentAccount implements PersistablePayload {
processValidationResult(new LengthValidator(2, 100).validate(value)); processValidationResult(new LengthValidator(2, 100).validate(value));
break; break;
case ACCOUNT_TYPE: case ACCOUNT_TYPE:
throw new IllegalArgumentException("Not implemented"); processValidationResult(new LengthValidator(2, 100).validate(value));
break;
case ANSWER: case ANSWER:
throw new IllegalArgumentException("Not implemented"); throw new IllegalArgumentException("Not implemented");
case BANK_ACCOUNT_NAME: case BANK_ACCOUNT_NAME:
@ -491,7 +492,8 @@ public abstract class PaymentAccount implements PersistablePayload {
processValidationResult(new BICValidator().validate(value)); processValidationResult(new BICValidator().validate(value));
break; break;
case BRANCH_ID: case BRANCH_ID:
throw new IllegalArgumentException("Not implemented"); processValidationResult(new LengthValidator(2, 34).validate(value));
break;
case CITY: case CITY:
processValidationResult(new LengthValidator(2, 34).validate(value)); processValidationResult(new LengthValidator(2, 34).validate(value));
break; break;
@ -518,7 +520,8 @@ public abstract class PaymentAccount implements PersistablePayload {
case EXTRA_INFO: case EXTRA_INFO:
break; break;
case HOLDER_ADDRESS: case HOLDER_ADDRESS:
throw new IllegalArgumentException("Not implemented"); processValidationResult(new LengthValidator(0, 100).validate(value));
break;
case HOLDER_EMAIL: case HOLDER_EMAIL:
throw new IllegalArgumentException("Not implemented"); throw new IllegalArgumentException("Not implemented");
case HOLDER_NAME: case HOLDER_NAME:
@ -623,9 +626,13 @@ public abstract class PaymentAccount implements PersistablePayload {
field.setLabel(Res.get("payment.account.owner")); field.setLabel(Res.get("payment.account.owner"));
break; break;
case ACCOUNT_TYPE: case ACCOUNT_TYPE:
throw new IllegalArgumentException("Not implemented"); field.setComponent(PaymentAccountFormField.Component.SELECT_ONE);
field.setLabel(Res.get("payment.select.account"));
break;
case ANSWER: case ANSWER:
throw new IllegalArgumentException("Not implemented"); field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.answer"));
break;
case BANK_ACCOUNT_NAME: case BANK_ACCOUNT_NAME:
field.setComponent(PaymentAccountFormField.Component.TEXT); field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.account.owner")); field.setLabel(Res.get("payment.account.owner"));
@ -668,11 +675,11 @@ public abstract class PaymentAccount implements PersistablePayload {
break; break;
case BENEFICIARY_ACCOUNT_NR: case BENEFICIARY_ACCOUNT_NR:
field.setComponent(PaymentAccountFormField.Component.TEXT); field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.swift.account")); field.setLabel(Res.get("payment.swift.account")); // TODO: this is specific to swift
break; break;
case BENEFICIARY_ADDRESS: case BENEFICIARY_ADDRESS:
field.setComponent(PaymentAccountFormField.Component.TEXTAREA); field.setComponent(PaymentAccountFormField.Component.TEXTAREA);
field.setLabel(Res.get("payment.swift.address.beneficiary")); field.setLabel(Res.get("payment.swift.address.beneficiary")); // TODO: this is specific to swift
break; break;
case BENEFICIARY_CITY: case BENEFICIARY_CITY:
field.setComponent(PaymentAccountFormField.Component.TEXT); field.setComponent(PaymentAccountFormField.Component.TEXT);
@ -691,7 +698,9 @@ public abstract class PaymentAccount implements PersistablePayload {
field.setLabel("BIC"); field.setLabel("BIC");
break; break;
case BRANCH_ID: case BRANCH_ID:
throw new IllegalArgumentException("Not implemented"); field.setComponent(PaymentAccountFormField.Component.TEXT);
//field.setLabel("Not implemented"); // expected to be overridden by subclasses
break;
case CITY: case CITY:
field.setComponent(PaymentAccountFormField.Component.TEXT); field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.account.city")); field.setLabel(Res.get("payment.account.city"));
@ -717,7 +726,9 @@ public abstract class PaymentAccount implements PersistablePayload {
field.setLabel(Res.get("payment.shared.optionalExtra")); field.setLabel(Res.get("payment.shared.optionalExtra"));
break; break;
case HOLDER_ADDRESS: case HOLDER_ADDRESS:
throw new IllegalArgumentException("Not implemented"); field.setComponent(PaymentAccountFormField.Component.TEXTAREA);
field.setLabel(Res.get("payment.account.owner.address"));
break;
case HOLDER_EMAIL: case HOLDER_EMAIL:
throw new IllegalArgumentException("Not implemented"); throw new IllegalArgumentException("Not implemented");
case HOLDER_NAME: case HOLDER_NAME:
@ -755,7 +766,9 @@ public abstract class PaymentAccount implements PersistablePayload {
field.setLabel(Res.get("payment.swift.swiftCode.intermediary")); field.setLabel(Res.get("payment.swift.swiftCode.intermediary"));
break; break;
case MOBILE_NR: case MOBILE_NR:
throw new IllegalArgumentException("Not implemented"); field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.mobile"));
break;
case NATIONAL_ACCOUNT_ID: case NATIONAL_ACCOUNT_ID:
throw new IllegalArgumentException("Not implemented"); throw new IllegalArgumentException("Not implemented");
case PAYID: case PAYID:
@ -771,7 +784,9 @@ public abstract class PaymentAccount implements PersistablePayload {
case PROMPT_PAY_ID: case PROMPT_PAY_ID:
throw new IllegalArgumentException("Not implemented"); throw new IllegalArgumentException("Not implemented");
case QUESTION: case QUESTION:
throw new IllegalArgumentException("Not implemented"); field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.secret"));
break;
case REQUIREMENTS: case REQUIREMENTS:
throw new IllegalArgumentException("Not implemented"); throw new IllegalArgumentException("Not implemented");
case SALT: case SALT:

View file

@ -50,6 +50,7 @@ import static haveno.common.util.Utilities.decodeFromHex;
import static haveno.core.locale.CountryUtil.findCountryByCode; import static haveno.core.locale.CountryUtil.findCountryByCode;
import static haveno.core.locale.CurrencyUtil.getTradeCurrenciesInList; import static haveno.core.locale.CurrencyUtil.getTradeCurrenciesInList;
import static haveno.core.locale.CurrencyUtil.getTradeCurrency; import static haveno.core.locale.CurrencyUtil.getTradeCurrency;
import static haveno.core.payment.payload.PaymentMethod.AMAZON_GIFT_CARD_ID;
import static haveno.core.payment.payload.PaymentMethod.MONEY_GRAM_ID; import static haveno.core.payment.payload.PaymentMethod.MONEY_GRAM_ID;
import static java.lang.String.format; import static java.lang.String.format;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
@ -438,6 +439,8 @@ class PaymentAccountTypeAdapter extends TypeAdapter<PaymentAccount> {
// account.setSingleTradeCurrency(fiatCurrency); // account.setSingleTradeCurrency(fiatCurrency);
} else if (account.hasPaymentMethodWithId(MONEY_GRAM_ID)) { } else if (account.hasPaymentMethodWithId(MONEY_GRAM_ID)) {
((MoneyGramAccount) account).setCountry(country.get()); ((MoneyGramAccount) account).setCountry(country.get());
} else if (account.hasPaymentMethodWithId(AMAZON_GIFT_CARD_ID)) {
((AmazonGiftCardAccount) account).setCountry(country.get());
} else { } else {
String errMsg = format("cannot set the country on a %s", String errMsg = format("cannot set the country on a %s",
paymentAccountType.getSimpleName()); paymentAccountType.getSimpleName());

View file

@ -17,12 +17,14 @@
package haveno.core.payment; package haveno.core.payment;
import haveno.core.api.model.PaymentAccountForm;
import haveno.core.api.model.PaymentAccountFormField; import haveno.core.api.model.PaymentAccountFormField;
import haveno.core.locale.TraditionalCurrency; import haveno.core.locale.TraditionalCurrency;
import haveno.core.locale.TradeCurrency; import haveno.core.locale.TradeCurrency;
import haveno.core.payment.payload.PaymentAccountPayload; import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.payment.payload.PaymentMethod; import haveno.core.payment.payload.PaymentMethod;
import haveno.core.payment.payload.SwishAccountPayload; import haveno.core.payment.payload.SwishAccountPayload;
import haveno.core.payment.validation.SwishValidator;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NonNull; import lombok.NonNull;
@ -33,6 +35,13 @@ public final class SwishAccount extends PaymentAccount {
public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("SEK")); public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("SEK"));
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.MOBILE_NR,
PaymentAccountFormField.FieldId.HOLDER_NAME,
PaymentAccountFormField.FieldId.SALT
);
public SwishAccount() { public SwishAccount() {
super(PaymentMethod.SWISH); super(PaymentMethod.SWISH);
setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0)); setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0));
@ -50,7 +59,7 @@ public final class SwishAccount extends PaymentAccount {
@Override @Override
public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() { public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented"); return INPUT_FIELD_IDS;
} }
public void setMobileNr(String mobileNr) { public void setMobileNr(String mobileNr) {
@ -68,4 +77,16 @@ public final class SwishAccount extends PaymentAccount {
public String getHolderName() { public String getHolderName() {
return ((SwishAccountPayload) paymentAccountPayload).getHolderName(); return ((SwishAccountPayload) paymentAccountPayload).getHolderName();
} }
@Override
public void validateFormField(PaymentAccountForm form, PaymentAccountFormField.FieldId fieldId, String value) {
switch (fieldId) {
case MOBILE_NR:
processValidationResult(new SwishValidator().validate(value));
break;
default:
super.validateFormField(form, fieldId, value);
break;
}
}
} }

View file

@ -19,6 +19,7 @@ package haveno.core.payment;
import haveno.core.api.model.PaymentAccountFormField; import haveno.core.api.model.PaymentAccountFormField;
import haveno.core.locale.TraditionalCurrency; import haveno.core.locale.TraditionalCurrency;
import haveno.core.locale.Res;
import haveno.core.locale.TradeCurrency; import haveno.core.locale.TradeCurrency;
import haveno.core.payment.payload.PaymentAccountPayload; import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.payment.payload.PaymentMethod; import haveno.core.payment.payload.PaymentMethod;
@ -33,6 +34,15 @@ public final class TransferwiseUsdAccount extends CountryBasedPaymentAccount {
public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("USD")); public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("USD"));
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.EMAIL,
PaymentAccountFormField.FieldId.HOLDER_NAME,
PaymentAccountFormField.FieldId.HOLDER_ADDRESS,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.COUNTRY,
PaymentAccountFormField.FieldId.SALT
);
public TransferwiseUsdAccount() { public TransferwiseUsdAccount() {
super(PaymentMethod.TRANSFERWISE_USD); super(PaymentMethod.TRANSFERWISE_USD);
// this payment method is currently restricted to United States/USD // this payment method is currently restricted to United States/USD
@ -61,11 +71,11 @@ public final class TransferwiseUsdAccount extends CountryBasedPaymentAccount {
} }
public void setBeneficiaryAddress(String address) { public void setBeneficiaryAddress(String address) {
((TransferwiseUsdAccountPayload) paymentAccountPayload).setBeneficiaryAddress(address); ((TransferwiseUsdAccountPayload) paymentAccountPayload).setHolderAddress(address);
} }
public String getBeneficiaryAddress() { public String getBeneficiaryAddress() {
return ((TransferwiseUsdAccountPayload) paymentAccountPayload).getBeneficiaryAddress(); return ((TransferwiseUsdAccountPayload) paymentAccountPayload).getHolderAddress();
} }
@Override @Override
@ -90,6 +100,13 @@ public final class TransferwiseUsdAccount extends CountryBasedPaymentAccount {
@Override @Override
public @NotNull List<PaymentAccountFormField.FieldId> getInputFieldIds() { public @NotNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented"); return INPUT_FIELD_IDS;
}
@Override
protected PaymentAccountFormField getEmptyFormField(PaymentAccountFormField.FieldId fieldId) {
var field = super.getEmptyFormField(fieldId);
if (field.getId() == PaymentAccountFormField.FieldId.HOLDER_ADDRESS) field.setLabel(field.getLabel() + " " + Res.get("payment.transferwiseUsd.address"));
return field;
} }
} }

View file

@ -33,6 +33,13 @@ public final class USPostalMoneyOrderAccount extends PaymentAccount {
public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("USD")); public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(new TraditionalCurrency("USD"));
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.HOLDER_NAME,
PaymentAccountFormField.FieldId.POSTAL_ADDRESS,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.SALT
);
public USPostalMoneyOrderAccount() { public USPostalMoneyOrderAccount() {
super(PaymentMethod.US_POSTAL_MONEY_ORDER); super(PaymentMethod.US_POSTAL_MONEY_ORDER);
setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0)); setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0));
@ -50,7 +57,7 @@ public final class USPostalMoneyOrderAccount extends PaymentAccount {
@Override @Override
public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() { public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented"); return INPUT_FIELD_IDS;
} }
public void setPostalAddress(String postalAddress) { public void setPostalAddress(String postalAddress) {

View file

@ -38,6 +38,13 @@ public final class WeChatPayAccount extends PaymentAccount {
new TraditionalCurrency("GBP") new TraditionalCurrency("GBP")
); );
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.ACCOUNT_NR,
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
PaymentAccountFormField.FieldId.SALT
);
public WeChatPayAccount() { public WeChatPayAccount() {
super(PaymentMethod.WECHAT_PAY); super(PaymentMethod.WECHAT_PAY);
} }
@ -54,7 +61,7 @@ public final class WeChatPayAccount extends PaymentAccount {
@Override @Override
public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() { public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented"); return INPUT_FIELD_IDS;
} }
public void setAccountNr(String accountNr) { public void setAccountNr(String accountNr) {

View file

@ -36,7 +36,7 @@ import java.util.Map;
@Getter @Getter
@Slf4j @Slf4j
public final class InteracETransferAccountPayload extends PaymentAccountPayload implements PayloadWithHolderName { public final class InteracETransferAccountPayload extends PaymentAccountPayload implements PayloadWithHolderName {
private String email = ""; private String emailOrMobileNr = "";
private String holderName = ""; private String holderName = "";
private String question = ""; private String question = "";
private String answer = ""; private String answer = "";
@ -52,7 +52,7 @@ public final class InteracETransferAccountPayload extends PaymentAccountPayload
private InteracETransferAccountPayload(String paymentMethod, private InteracETransferAccountPayload(String paymentMethod,
String id, String id,
String email, String emailOrMobileNr,
String holderName, String holderName,
String question, String question,
String answer, String answer,
@ -62,7 +62,7 @@ public final class InteracETransferAccountPayload extends PaymentAccountPayload
id, id,
maxTradePeriod, maxTradePeriod,
excludeFromJsonDataMap); excludeFromJsonDataMap);
this.email = email; this.emailOrMobileNr = emailOrMobileNr;
this.holderName = holderName; this.holderName = holderName;
this.question = question; this.question = question;
this.answer = answer; this.answer = answer;
@ -72,7 +72,7 @@ public final class InteracETransferAccountPayload extends PaymentAccountPayload
public Message toProtoMessage() { public Message toProtoMessage() {
return getPaymentAccountPayloadBuilder() return getPaymentAccountPayloadBuilder()
.setInteracETransferAccountPayload(protobuf.InteracETransferAccountPayload.newBuilder() .setInteracETransferAccountPayload(protobuf.InteracETransferAccountPayload.newBuilder()
.setEmail(email) .setEmailOrMobileNr(emailOrMobileNr)
.setHolderName(holderName) .setHolderName(holderName)
.setQuestion(question) .setQuestion(question)
.setAnswer(answer)) .setAnswer(answer))
@ -82,7 +82,7 @@ public final class InteracETransferAccountPayload extends PaymentAccountPayload
public static InteracETransferAccountPayload fromProto(protobuf.PaymentAccountPayload proto) { public static InteracETransferAccountPayload fromProto(protobuf.PaymentAccountPayload proto) {
return new InteracETransferAccountPayload(proto.getPaymentMethodId(), return new InteracETransferAccountPayload(proto.getPaymentMethodId(),
proto.getId(), proto.getId(),
proto.getInteracETransferAccountPayload().getEmail(), proto.getInteracETransferAccountPayload().getEmailOrMobileNr(),
proto.getInteracETransferAccountPayload().getHolderName(), proto.getInteracETransferAccountPayload().getHolderName(),
proto.getInteracETransferAccountPayload().getQuestion(), proto.getInteracETransferAccountPayload().getQuestion(),
proto.getInteracETransferAccountPayload().getAnswer(), proto.getInteracETransferAccountPayload().getAnswer(),
@ -98,21 +98,21 @@ public final class InteracETransferAccountPayload extends PaymentAccountPayload
@Override @Override
public String getPaymentDetails() { public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.owner") + " " + holderName + ", " + return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.owner") + " " + holderName + ", " +
Res.get("payment.email") + " " + email + ", " + Res.getWithCol("payment.secret") + " " + Res.get("payment.email") + " " + emailOrMobileNr + ", " + Res.getWithCol("payment.secret") + " " +
question + ", " + Res.getWithCol("payment.answer") + " " + answer; question + ", " + Res.getWithCol("payment.answer") + " " + answer;
} }
@Override @Override
public String getPaymentDetailsForTradePopup() { public String getPaymentDetailsForTradePopup() {
return Res.getWithCol("payment.account.owner") + " " + holderName + "\n" + return Res.getWithCol("payment.account.owner") + " " + holderName + "\n" +
Res.getWithCol("payment.email") + " " + email + "\n" + Res.getWithCol("payment.email") + " " + emailOrMobileNr + "\n" +
Res.getWithCol("payment.secret") + " " + question + "\n" + Res.getWithCol("payment.secret") + " " + question + "\n" +
Res.getWithCol("payment.answer") + " " + answer; Res.getWithCol("payment.answer") + " " + answer;
} }
@Override @Override
public byte[] getAgeWitnessInputData() { public byte[] getAgeWitnessInputData() {
return super.getAgeWitnessInputData(ArrayUtils.addAll(email.getBytes(StandardCharsets.UTF_8), return super.getAgeWitnessInputData(ArrayUtils.addAll(emailOrMobileNr.getBytes(StandardCharsets.UTF_8),
ArrayUtils.addAll(question.getBytes(StandardCharsets.UTF_8), ArrayUtils.addAll(question.getBytes(StandardCharsets.UTF_8),
answer.getBytes(StandardCharsets.UTF_8)))); answer.getBytes(StandardCharsets.UTF_8))));
} }

View file

@ -369,7 +369,15 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
CASH_APP_ID, CASH_APP_ID,
PAYPAL_ID, PAYPAL_ID,
VENMO_ID, VENMO_ID,
PAYSAFE_ID); PAYSAFE_ID,
WECHAT_PAY_ID,
ALI_PAY_ID,
SWISH_ID,
TRANSFERWISE_USD_ID,
AMAZON_GIFT_CARD_ID,
ACH_TRANSFER_ID,
INTERAC_E_TRANSFER_ID,
US_POSTAL_MONEY_ORDER_ID);
return paymentMethods.stream().filter(paymentMethod -> paymentMethodIds.contains(paymentMethod.getId())).collect(Collectors.toList()); return paymentMethods.stream().filter(paymentMethod -> paymentMethodIds.contains(paymentMethod.getId())).collect(Collectors.toList());
} }

View file

@ -39,7 +39,7 @@ import java.util.Map;
public final class TransferwiseUsdAccountPayload extends CountryBasedPaymentAccountPayload { public final class TransferwiseUsdAccountPayload extends CountryBasedPaymentAccountPayload {
private String email = ""; private String email = "";
private String holderName = ""; private String holderName = "";
private String beneficiaryAddress = ""; private String holderAddress = "";
public TransferwiseUsdAccountPayload(String paymentMethod, String id) { public TransferwiseUsdAccountPayload(String paymentMethod, String id) {
super(paymentMethod, id); super(paymentMethod, id);
@ -51,7 +51,7 @@ public final class TransferwiseUsdAccountPayload extends CountryBasedPaymentAcco
List<String> acceptedCountryCodes, List<String> acceptedCountryCodes,
String email, String email,
String holderName, String holderName,
String beneficiaryAddress, String holderAddress,
long maxTradePeriod, long maxTradePeriod,
Map<String, String> excludeFromJsonDataMap) { Map<String, String> excludeFromJsonDataMap) {
super(paymentMethod, super(paymentMethod,
@ -63,7 +63,7 @@ public final class TransferwiseUsdAccountPayload extends CountryBasedPaymentAcco
this.email = email; this.email = email;
this.holderName = holderName; this.holderName = holderName;
this.beneficiaryAddress = beneficiaryAddress; this.holderAddress = holderAddress;
} }
@Override @Override
@ -71,7 +71,7 @@ public final class TransferwiseUsdAccountPayload extends CountryBasedPaymentAcco
protobuf.TransferwiseUsdAccountPayload.Builder builder = protobuf.TransferwiseUsdAccountPayload.newBuilder() protobuf.TransferwiseUsdAccountPayload.Builder builder = protobuf.TransferwiseUsdAccountPayload.newBuilder()
.setEmail(email) .setEmail(email)
.setHolderName(holderName) .setHolderName(holderName)
.setBeneficiaryAddress(beneficiaryAddress); .setHolderAddress(holderAddress);
final protobuf.CountryBasedPaymentAccountPayload.Builder countryBasedPaymentAccountPayload = getPaymentAccountPayloadBuilder() final protobuf.CountryBasedPaymentAccountPayload.Builder countryBasedPaymentAccountPayload = getPaymentAccountPayloadBuilder()
.getCountryBasedPaymentAccountPayloadBuilder() .getCountryBasedPaymentAccountPayloadBuilder()
.setTransferwiseUsdAccountPayload(builder); .setTransferwiseUsdAccountPayload(builder);
@ -89,7 +89,7 @@ public final class TransferwiseUsdAccountPayload extends CountryBasedPaymentAcco
new ArrayList<>(countryBasedPaymentAccountPayload.getAcceptedCountryCodesList()), new ArrayList<>(countryBasedPaymentAccountPayload.getAcceptedCountryCodesList()),
accountPayloadPB.getEmail(), accountPayloadPB.getEmail(),
accountPayloadPB.getHolderName(), accountPayloadPB.getHolderName(),
accountPayloadPB.getBeneficiaryAddress(), accountPayloadPB.getHolderAddress(),
proto.getMaxTradePeriod(), proto.getMaxTradePeriod(),
new HashMap<>(proto.getExcludeFromJsonDataMap())); new HashMap<>(proto.getExcludeFromJsonDataMap()));
} }

View file

@ -31,6 +31,7 @@ import haveno.common.file.FileUtil;
import haveno.common.util.Base64; import haveno.common.util.Base64;
import haveno.common.util.Utilities; import haveno.common.util.Utilities;
import haveno.core.api.CoreNotificationService; import haveno.core.api.CoreNotificationService;
import haveno.core.api.CorePaymentAccountsService;
import haveno.core.api.XmrConnectionService; import haveno.core.api.XmrConnectionService;
import haveno.core.app.HavenoSetup; import haveno.core.app.HavenoSetup;
import haveno.core.offer.OfferPayload; import haveno.core.offer.OfferPayload;
@ -132,6 +133,7 @@ public class HavenoUtils {
public static XmrConnectionService xmrConnectionService; public static XmrConnectionService xmrConnectionService;
public static OpenOfferManager openOfferManager; public static OpenOfferManager openOfferManager;
public static CoreNotificationService notificationService; public static CoreNotificationService notificationService;
public static CorePaymentAccountsService corePaymentAccountService;
public static Preferences preferences; public static Preferences preferences;
public static boolean isSeedNode() { public static boolean isSeedNode() {

View file

@ -120,7 +120,7 @@ public class AmazonGiftCardForm extends PaymentMethodForm {
@Override @Override
protected void autoFillNameTextField() { protected void autoFillNameTextField() {
setAccountNameWithString(amazonGiftCardAccount.getEmailOrMobileNr()); setAccountNameWithString(amazonGiftCardAccount.getEmailOrMobileNr() == null ? "" : amazonGiftCardAccount.getEmailOrMobileNr());
} }
@Override @Override

View file

@ -44,7 +44,7 @@ public class InteracETransferForm extends PaymentMethodForm {
addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.account.owner"), addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.account.owner"),
((InteracETransferAccountPayload) paymentAccountPayload).getHolderName()); ((InteracETransferAccountPayload) paymentAccountPayload).getHolderName());
addCompactTopLabelTextField(gridPane, gridRow, 1, Res.get("payment.emailOrMobile"), addCompactTopLabelTextField(gridPane, gridRow, 1, Res.get("payment.emailOrMobile"),
((InteracETransferAccountPayload) paymentAccountPayload).getEmail()); ((InteracETransferAccountPayload) paymentAccountPayload).getEmailOrMobileNr());
addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.secret"), addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.secret"),
((InteracETransferAccountPayload) paymentAccountPayload).getQuestion()); ((InteracETransferAccountPayload) paymentAccountPayload).getQuestion());
addCompactTopLabelTextField(gridPane, gridRow, 1, Res.get("payment.answer"), addCompactTopLabelTextField(gridPane, gridRow, 1, Res.get("payment.answer"),

View file

@ -57,7 +57,7 @@ public class TransferwiseUsdForm extends PaymentMethodForm {
addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, 1, Res.get("payment.email"), addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, 1, Res.get("payment.email"),
((TransferwiseUsdAccountPayload) paymentAccountPayload).getEmail()); ((TransferwiseUsdAccountPayload) paymentAccountPayload).getEmail());
String address = ((TransferwiseUsdAccountPayload) paymentAccountPayload).getBeneficiaryAddress(); String address = ((TransferwiseUsdAccountPayload) paymentAccountPayload).getHolderAddress();
if (address.length() > 0) { if (address.length() > 0) {
TextArea textAddress = addCompactTopLabelTextArea(gridPane, gridRow, 0, Res.get("payment.account.address"), "").second; TextArea textAddress = addCompactTopLabelTextArea(gridPane, gridRow, 0, Res.get("payment.account.address"), "").second;
textAddress.setMinHeight(70); textAddress.setMinHeight(70);
@ -96,7 +96,7 @@ public class TransferwiseUsdForm extends PaymentMethodForm {
updateFromInputs(); updateFromInputs();
}); });
String addressLabel = Res.get("payment.account.owner.address") + Res.get("payment.transferwiseUsd.address"); String addressLabel = Res.get("payment.account.owner.address") + " " + Res.get("payment.transferwiseUsd.address");
TextArea addressTextArea = addTopLabelTextArea(gridPane, ++gridRow, addressLabel, addressLabel).second; TextArea addressTextArea = addTopLabelTextArea(gridPane, ++gridRow, addressLabel, addressLabel).second;
addressTextArea.setMinHeight(70); addressTextArea.setMinHeight(70);
addressTextArea.textProperty().addListener((ov, oldValue, newValue) -> { addressTextArea.textProperty().addListener((ov, oldValue, newValue) -> {

View file

@ -165,7 +165,7 @@ public class LockedView extends ActivatableView<VBox, Void> {
numItems.setText(Res.get("shared.numItemsLabel", sortedList.size())); numItems.setText(Res.get("shared.numItemsLabel", sortedList.size()));
exportButton.setOnAction(event -> { exportButton.setOnAction(event -> {
ObservableList<TableColumn<LockedListItem, ?>> tableColumns = tableView.getColumns(); ObservableList<TableColumn<LockedListItem, ?>> tableColumns = GUIUtil.getContentColumns(tableView);
int reportColumns = tableColumns.size(); int reportColumns = tableColumns.size();
CSVEntryConverter<LockedListItem> headerConverter = item -> { CSVEntryConverter<LockedListItem> headerConverter = item -> {
String[] columns = new String[reportColumns]; String[] columns = new String[reportColumns];

View file

@ -165,7 +165,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
numItems.setText(Res.get("shared.numItemsLabel", sortedList.size())); numItems.setText(Res.get("shared.numItemsLabel", sortedList.size()));
exportButton.setOnAction(event -> { exportButton.setOnAction(event -> {
ObservableList<TableColumn<ReservedListItem, ?>> tableColumns = tableView.getColumns(); ObservableList<TableColumn<ReservedListItem, ?>> tableColumns = GUIUtil.getContentColumns(tableView);
int reportColumns = tableColumns.size(); int reportColumns = tableColumns.size();
CSVEntryConverter<ReservedListItem> headerConverter = item -> { CSVEntryConverter<ReservedListItem> headerConverter = item -> {
String[] columns = new String[reportColumns]; String[] columns = new String[reportColumns];

View file

@ -203,7 +203,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
numItems.setText(Res.get("shared.numItemsLabel", sortedDisplayedTransactions.size())); numItems.setText(Res.get("shared.numItemsLabel", sortedDisplayedTransactions.size()));
exportButton.setOnAction(event -> { exportButton.setOnAction(event -> {
final ObservableList<TableColumn<TransactionsListItem, ?>> tableColumns = tableView.getColumns(); final ObservableList<TableColumn<TransactionsListItem, ?>> tableColumns = GUIUtil.getContentColumns(tableView);
final int reportColumns = tableColumns.size() - 1; // CSV report excludes the last column (an icon) final int reportColumns = tableColumns.size() - 1; // CSV report excludes the last column (an icon)
CSVEntryConverter<TransactionsListItem> headerConverter = item -> { CSVEntryConverter<TransactionsListItem> headerConverter = item -> {
String[] columns = new String[reportColumns]; String[] columns = new String[reportColumns];

View file

@ -397,7 +397,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
} }
private void exportToCsv() { private void exportToCsv() {
ObservableList<TableColumn<TradeStatistics3ListItem, ?>> tableColumns = tableView.getColumns(); ObservableList<TableColumn<TradeStatistics3ListItem, ?>> tableColumns = GUIUtil.getContentColumns(tableView);
int reportColumns = tableColumns.size() + 1; int reportColumns = tableColumns.size() + 1;
boolean showAllTradeCurrencies = model.showAllTradeCurrenciesProperty.get(); boolean showAllTradeCurrencies = model.showAllTradeCurrenciesProperty.get();

View file

@ -1281,6 +1281,16 @@ public class GUIUtil {
} }
} }
public static <T> ObservableList<TableColumn<T, ?>> getContentColumns(TableView<T> tableView) {
ObservableList<TableColumn<T, ?>> contentColumns = FXCollections.observableArrayList();
for (TableColumn<T, ?> column : tableView.getColumns()) {
if (!column.getStyleClass().contains("first-column") && !column.getStyleClass().contains("last-column")) {
contentColumns.add(column);
}
}
return contentColumns;
}
public static ImageView getCurrencyIcon(String currencyCode) { public static ImageView getCurrencyIcon(String currencyCode) {
return getCurrencyIcon(currencyCode, 24); return getCurrencyIcon(currencyCode, 24);
} }

View file

@ -1047,7 +1047,7 @@ message FasterPaymentsAccountPayload {
} }
message InteracETransferAccountPayload { message InteracETransferAccountPayload {
string email = 1; string email_or_mobile_nr = 1;
string holder_name = 2; string holder_name = 2;
string question = 3; string question = 3;
string answer = 4; string answer = 4;
@ -1163,7 +1163,7 @@ message TransferwiseAccountPayload {
message TransferwiseUsdAccountPayload { message TransferwiseUsdAccountPayload {
string email = 1; string email = 1;
string holder_name = 2; string holder_name = 2;
string beneficiary_address = 3; string holder_address = 3;
} }
message PayseraAccountPayload { message PayseraAccountPayload {
@ -1903,6 +1903,14 @@ message PaymentAccountForm {
PAYPAL = 17; PAYPAL = 17;
VENMO = 18; VENMO = 18;
PAYSAFE = 19; PAYSAFE = 19;
WECHAT_PAY = 20;
ALI_PAY = 21;
SWISH = 22;
TRANSFERWISE_USD = 23;
AMAZON_GIFT_CARD = 24;
ACH_TRANSFER = 25;
INTERAC_E_TRANSFER = 26;
US_POSTAL_MONEY_ORDER = 27;
} }
FormId id = 1; FormId id = 1;
repeated PaymentAccountFormField fields = 2; repeated PaymentAccountFormField fields = 2;