support interac e-transfer payment accounts over grpc api

This commit is contained in:
woodser 2025-06-20 18:41:16 -04:00
parent ae650c6ee1
commit 5fb92848c2
No known key found for this signature in database
GPG key ID: 55A10DD48ADEE5EF
9 changed files with 65 additions and 19 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

@ -84,7 +84,8 @@ public final class PaymentAccountForm implements PersistablePayload {
SWISH, SWISH,
TRANSFERWISE_USD, TRANSFERWISE_USD,
AMAZON_GIFT_CARD, AMAZON_GIFT_CARD,
ACH_TRANSFER; ACH_TRANSFER,
INTERAC_E_TRANSFER;
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

@ -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

@ -630,7 +630,9 @@ public abstract class PaymentAccount implements PersistablePayload {
field.setLabel(Res.get("payment.select.account")); field.setLabel(Res.get("payment.select.account"));
break; 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"));
@ -782,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

@ -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

@ -375,7 +375,8 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
SWISH_ID, SWISH_ID,
TRANSFERWISE_USD_ID, TRANSFERWISE_USD_ID,
AMAZON_GIFT_CARD_ID, AMAZON_GIFT_CARD_ID,
ACH_TRANSFER_ID); ACH_TRANSFER_ID,
INTERAC_E_TRANSFER_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

@ -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

@ -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

@ -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;
@ -1909,6 +1909,7 @@ message PaymentAccountForm {
TRANSFERWISE_USD = 23; TRANSFERWISE_USD = 23;
AMAZON_GIFT_CARD = 24; AMAZON_GIFT_CARD = 24;
ACH_TRANSFER = 25; ACH_TRANSFER = 25;
INTERAC_E_TRANSFER = 26;
} }
FormId id = 1; FormId id = 1;
repeated PaymentAccountFormField fields = 2; repeated PaymentAccountFormField fields = 2;