support paypal, cashapp, venmo

Co-authored-by: preland <89992615+preland@users.noreply.github.com>
This commit is contained in:
woodser 2024-06-08 11:34:35 -04:00
parent 26c32a8ff4
commit fea804086b
51 changed files with 881 additions and 186 deletions

View file

@ -74,7 +74,10 @@ public final class PaymentAccountForm implements PersistablePayload {
TRANSFERWISE,
UPHOLD,
ZELLE,
AUSTRALIA_PAYID;
AUSTRALIA_PAYID,
CASH_APP,
PAYPAL,
VENMO;
public static PaymentAccountForm.FormId fromProto(protobuf.PaymentAccountForm.FormId formId) {
return ProtoUtil.enumFromProto(PaymentAccountForm.FormId.class, formId.name());

View file

@ -98,7 +98,9 @@ public final class PaymentAccountFormField implements PersistablePayload {
SPECIAL_INSTRUCTIONS,
STATE,
TRADE_CURRENCIES,
USER_NAME;
USERNAME,
EMAIL_OR_MOBILE_NR_OR_USERNAME,
EMAIL_OR_MOBILE_NR_OR_CASHTAG;
public static PaymentAccountFormField.FieldId fromProto(protobuf.PaymentAccountFormField.FieldId fieldId) {
return ProtoUtil.enumFromProto(PaymentAccountFormField.FieldId.class, fieldId.name());

View file

@ -217,7 +217,7 @@ public class DomainInitialisation {
revolutAccountsUpdateHandler.accept(user.getPaymentAccountsAsObservable().stream()
.filter(paymentAccount -> paymentAccount instanceof RevolutAccount)
.map(paymentAccount -> (RevolutAccount) paymentAccount)
.filter(RevolutAccount::userNameNotSet)
.filter(RevolutAccount::usernameNotSet)
.collect(Collectors.toList()));
}
if (amazonGiftCardAccountsUpdateHandler != null && user.getPaymentAccountsAsObservable() != null) {

View file

@ -28,17 +28,22 @@ import lombok.NonNull;
import java.util.List;
// Removed due too high chargeback risk
// Cannot be deleted as it would break old trade history entries
@Deprecated
@EqualsAndHashCode(callSuper = true)
public final class CashAppAccount 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"),
new TraditionalCurrency("GBP"));
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR_OR_CASHTAG,
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.SALT);
public CashAppAccount() {
super(PaymentMethod.CASH_APP);
setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0));
tradeCurrencies.addAll(getSupportedCurrencies());
}
@Override
@ -53,14 +58,14 @@ public final class CashAppAccount extends PaymentAccount {
@Override
public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented");
return INPUT_FIELD_IDS;
}
public void setCashTag(String cashTag) {
((CashAppAccountPayload) paymentAccountPayload).setCashTag(cashTag);
public void setEmailOrMobileNrOrCashtag(String emailOrMobileNrOrCashtag) {
((CashAppAccountPayload) paymentAccountPayload).setEmailOrMobileNrOrCashtag(emailOrMobileNrOrCashtag);
}
public String getCashTag() {
return ((CashAppAccountPayload) paymentAccountPayload).getCashTag();
public String getEmailOrMobileNrOrCashtag() {
return ((CashAppAccountPayload) paymentAccountPayload).getEmailOrMobileNrOrCashtag();
}
}

View file

@ -0,0 +1,95 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package haveno.core.payment;
import haveno.core.api.model.PaymentAccountFormField;
import haveno.core.locale.TraditionalCurrency;
import haveno.core.locale.TradeCurrency;
import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.payment.payload.PaymentMethod;
import haveno.core.payment.payload.PayPalAccountPayload;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
public final class PayPalAccount extends PaymentAccount {
// https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/
public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(
new TraditionalCurrency("AUD"),
new TraditionalCurrency("BRL"),
new TraditionalCurrency("CAD"),
new TraditionalCurrency("CNY"),
new TraditionalCurrency("CZK"),
new TraditionalCurrency("DKK"),
new TraditionalCurrency("EUR"),
new TraditionalCurrency("HKD"),
new TraditionalCurrency("HUF"),
new TraditionalCurrency("ILS"),
new TraditionalCurrency("JPY"),
new TraditionalCurrency("MYR"),
new TraditionalCurrency("MXN"),
new TraditionalCurrency("TWD"),
new TraditionalCurrency("NZD"),
new TraditionalCurrency("NOK"),
new TraditionalCurrency("PHP"),
new TraditionalCurrency("PLN"),
new TraditionalCurrency("GBP"),
new TraditionalCurrency("SGD"),
new TraditionalCurrency("SEK"),
new TraditionalCurrency("CHF"),
new TraditionalCurrency("THB"),
new TraditionalCurrency("USD"));
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR_OR_USERNAME,
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.SALT);
public PayPalAccount() {
super(PaymentMethod.PAYPAL);
tradeCurrencies.addAll(SUPPORTED_CURRENCIES);
}
@Override
protected PaymentAccountPayload createPayload() {
return new PayPalAccountPayload(paymentMethod.getId(), id);
}
@Override
public @NonNull List<TradeCurrency> getSupportedCurrencies() {
return SUPPORTED_CURRENCIES;
}
@Override
public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
return INPUT_FIELD_IDS;
}
public void setEmailOrMobileNrOrUsername(String emailOrMobileNrOrUsername) {
((PayPalAccountPayload) paymentAccountPayload)
.setEmailOrMobileNrOrUsername(emailOrMobileNrOrUsername);
}
public String getEmailOrMobileNrOrUsername() {
return ((PayPalAccountPayload) paymentAccountPayload).getEmailOrMobileNrOrUsername();
}
}

View file

@ -552,7 +552,13 @@ public abstract class PaymentAccount implements PersistablePayload {
Optional<List<TradeCurrency>> tradeCurrencies = CurrencyUtil.getTradeCurrenciesInList(currencyCodes, getSupportedCurrencies());
if (!tradeCurrencies.isPresent()) throw new IllegalArgumentException("No trade currencies were found in the " + getPaymentMethod().getDisplayString() + " account form");
break;
case USER_NAME:
case USERNAME:
processValidationResult(new LengthValidator(3, 100).validate(value));
break;
case EMAIL_OR_MOBILE_NR_OR_USERNAME:
processValidationResult(new LengthValidator(3, 100).validate(value));
break;
case EMAIL_OR_MOBILE_NR_OR_CASHTAG:
processValidationResult(new LengthValidator(3, 100).validate(value));
break;
case ADDRESS:
@ -768,9 +774,21 @@ public abstract class PaymentAccount implements PersistablePayload {
field.setSupportedCurrencies(getSupportedCurrencies());
field.setValue(String.join(",", getSupportedCurrencies().stream().map(TradeCurrency::getCode).collect(Collectors.toList())));
break;
case USER_NAME:
case USERNAME:
field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.account.userName"));
field.setLabel(Res.get("payment.account.username"));
field.setMinLength(3);
field.setMaxLength(100);
break;
case EMAIL_OR_MOBILE_NR_OR_USERNAME:
field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.email.mobile.username"));
field.setMinLength(3);
field.setMaxLength(100);
break;
case EMAIL_OR_MOBILE_NR_OR_CASHTAG:
field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("payment.email.mobile.cashtag"));
field.setMinLength(3);
field.setMaxLength(100);
break;

View file

@ -86,6 +86,8 @@ public class PaymentAccountFactory {
return new TransferwiseAccount();
case PaymentMethod.TRANSFERWISE_USD_ID:
return new TransferwiseUsdAccount();
case PaymentMethod.PAYPAL_ID:
return new PayPalAccount();
case PaymentMethod.PAYSERA_ID:
return new PayseraAccount();
case PaymentMethod.PAXUM_ID:

View file

@ -66,6 +66,7 @@ import static haveno.core.payment.payload.PaymentMethod.NATIONAL_BANK_ID;
import static haveno.core.payment.payload.PaymentMethod.NEFT_ID;
import static haveno.core.payment.payload.PaymentMethod.NEQUI_ID;
import static haveno.core.payment.payload.PaymentMethod.PAXUM_ID;
import static haveno.core.payment.payload.PaymentMethod.PAYPAL_ID;
import static haveno.core.payment.payload.PaymentMethod.PAYSERA_ID;
import static haveno.core.payment.payload.PaymentMethod.PAYTM_ID;
import static haveno.core.payment.payload.PaymentMethod.PERFECT_MONEY_ID;
@ -214,6 +215,8 @@ public class PaymentAccountUtil {
return USPostalMoneyOrderAccount.SUPPORTED_CURRENCIES;
case VENMO_ID:
return VenmoAccount.SUPPORTED_CURRENCIES;
case PAYPAL_ID:
return PayPalAccount.SUPPORTED_CURRENCIES;
case JAPAN_BANK_ID:
return JapanBankAccount.SUPPORTED_CURRENCIES;
case WECHAT_PAY_ID:

View file

@ -32,7 +32,7 @@ import java.util.List;
public final class RevolutAccount extends PaymentAccount {
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.USER_NAME,
PaymentAccountFormField.FieldId.USERNAME,
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.SALT
@ -82,16 +82,16 @@ public final class RevolutAccount extends PaymentAccount {
return new RevolutAccountPayload(paymentMethod.getId(), id);
}
public void setUserName(String userName) {
revolutAccountPayload().setUserName(userName);
public void setUsername(String userame) {
revolutAccountPayload().setUserName(userame);
}
public String getUserName() {
return (revolutAccountPayload()).getUserName();
public String getUsername() {
return (revolutAccountPayload()).getUsername();
}
public boolean userNameNotSet() {
return (revolutAccountPayload()).userNameNotSet();
public boolean usernameNotSet() {
return (revolutAccountPayload()).usernameNotSet();
}
private RevolutAccountPayload revolutAccountPayload() {

View file

@ -28,14 +28,16 @@ import lombok.NonNull;
import java.util.List;
// Removed due too high chargeback risk
// Cannot be deleted as it would break old trade history entries
@Deprecated
@EqualsAndHashCode(callSuper = true)
public final class VenmoAccount extends PaymentAccount {
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_OR_MOBILE_NR_OR_USERNAME,
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.SALT);
public VenmoAccount() {
super(PaymentMethod.VENMO);
setSingleTradeCurrency(SUPPORTED_CURRENCIES.get(0));
@ -53,22 +55,15 @@ public final class VenmoAccount extends PaymentAccount {
@Override
public @NonNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
throw new RuntimeException("Not implemented");
return INPUT_FIELD_IDS;
}
public void setVenmoUserName(String venmoUserName) {
((VenmoAccountPayload) paymentAccountPayload).setVenmoUserName(venmoUserName);
public void setNameOrUsernameOrEmailOrMobileNr(String usernameOrEmailOrMobileNr) {
((VenmoAccountPayload) paymentAccountPayload)
.setEmailOrMobileNrOrUsername(usernameOrEmailOrMobileNr);
}
public String getVenmoUserName() {
return ((VenmoAccountPayload) paymentAccountPayload).getVenmoUserName();
}
public void setHolderName(String holderName) {
((VenmoAccountPayload) paymentAccountPayload).setHolderName(holderName);
}
public String getHolderName() {
return ((VenmoAccountPayload) paymentAccountPayload).getHolderName();
public String getNameOrUsernameOrEmailOrMobileNr() {
return ((VenmoAccountPayload) paymentAccountPayload).getEmailOrMobileNrOrUsername();
}
}

View file

@ -29,29 +29,25 @@ import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
// Cannot be deleted as it would break old trade history entries
// Removed due too high chargeback risk
@Deprecated
@EqualsAndHashCode(callSuper = true)
@ToString
@Setter
@Getter
@Slf4j
public final class CashAppAccountPayload extends PaymentAccountPayload {
private String cashTag = "";
private String emailOrMobileNrOrCashtag = "";
public CashAppAccountPayload(String paymentMethod, String id) {
super(paymentMethod, id);
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
private CashAppAccountPayload(String paymentMethod,
String id,
String cashTag,
String emailOrMobileNrOrCashtag,
long maxTradePeriod,
Map<String, String> excludeFromJsonDataMap) {
super(paymentMethod,
@ -59,21 +55,21 @@ public final class CashAppAccountPayload extends PaymentAccountPayload {
maxTradePeriod,
excludeFromJsonDataMap);
this.cashTag = cashTag;
this.emailOrMobileNrOrCashtag = emailOrMobileNrOrCashtag;
}
@Override
public Message toProtoMessage() {
return getPaymentAccountPayloadBuilder()
.setCashAppAccountPayload(protobuf.CashAppAccountPayload.newBuilder()
.setCashTag(cashTag))
.setEmailOrMobileNrOrCashtag(emailOrMobileNrOrCashtag))
.build();
}
public static CashAppAccountPayload fromProto(protobuf.PaymentAccountPayload proto) {
return new CashAppAccountPayload(proto.getPaymentMethodId(),
proto.getId(),
proto.getCashAppAccountPayload().getCashTag(),
proto.getCashAppAccountPayload().getEmailOrMobileNrOrCashtag(),
proto.getMaxTradePeriod(),
new HashMap<>(proto.getExcludeFromJsonDataMap()));
}
@ -85,7 +81,7 @@ public final class CashAppAccountPayload extends PaymentAccountPayload {
@Override
public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account") + " " + cashTag;
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.email.mobile.cashtag") + " " + emailOrMobileNrOrCashtag;
}
@Override
@ -95,6 +91,6 @@ public final class CashAppAccountPayload extends PaymentAccountPayload {
@Override
public byte[] getAgeWitnessInputData() {
return super.getAgeWitnessInputData(cashTag.getBytes(StandardCharsets.UTF_8));
return super.getAgeWitnessInputData(emailOrMobileNrOrCashtag.getBytes(StandardCharsets.UTF_8));
}
}

View file

@ -77,7 +77,7 @@ public final class MoneseAccountPayload extends PaymentAccountPayload {
@Override
public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.userName") + " " + holderName;
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.username") + " " + holderName;
}
@Override

View file

@ -0,0 +1,96 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package haveno.core.payment.payload;
import com.google.protobuf.Message;
import haveno.core.locale.Res;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@EqualsAndHashCode(callSuper = true)
@ToString
@Setter
@Getter
@Slf4j
public final class PayPalAccountPayload extends PaymentAccountPayload {
private String emailOrMobileNrOrUsername = "";
public PayPalAccountPayload(String paymentMethod, String id) {
super(paymentMethod, id);
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
private PayPalAccountPayload(String paymentMethod,
String id,
String emailOrMobileNrOrUsername,
long maxTradePeriod,
Map<String, String> excludeFromJsonDataMap) {
super(paymentMethod,
id,
maxTradePeriod,
excludeFromJsonDataMap);
this.emailOrMobileNrOrUsername = emailOrMobileNrOrUsername;
}
@Override
public Message toProtoMessage() {
return getPaymentAccountPayloadBuilder()
.setPaypalAccountPayload(protobuf.PayPalAccountPayload.newBuilder()
.setEmailOrMobileNrOrUsername(emailOrMobileNrOrUsername))
.build();
}
public static PayPalAccountPayload fromProto(protobuf.PaymentAccountPayload proto) {
return new PayPalAccountPayload(proto.getPaymentMethodId(),
proto.getId(),
proto.getPaypalAccountPayload().getEmailOrMobileNrOrUsername(),
proto.getMaxTradePeriod(),
new HashMap<>(proto.getExcludeFromJsonDataMap()));
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public String getPaymentDetails() {
return Res.getWithCol("payment.email.mobile.username") + " "
+ emailOrMobileNrOrUsername;
}
@Override
public String getPaymentDetailsForTradePopup() {
return getPaymentDetails();
}
@Override
public byte[] getAgeWitnessInputData() {
return super.getAgeWitnessInputData(emailOrMobileNrOrUsername.getBytes(StandardCharsets.UTF_8));
}
}

View file

@ -47,8 +47,10 @@ import haveno.core.payment.AmazonGiftCardAccount;
import haveno.core.payment.AustraliaPayidAccount;
import haveno.core.payment.BizumAccount;
import haveno.core.payment.CapitualAccount;
import haveno.core.payment.CashAppAccount;
import haveno.core.payment.CashAtAtmAccount;
import haveno.core.payment.PayByMailAccount;
import haveno.core.payment.PayPalAccount;
import haveno.core.payment.CashDepositAccount;
import haveno.core.payment.CelPayAccount;
import haveno.core.payment.ZelleAccount;
@ -89,6 +91,7 @@ import haveno.core.payment.TransferwiseUsdAccount;
import haveno.core.payment.USPostalMoneyOrderAccount;
import haveno.core.payment.UpholdAccount;
import haveno.core.payment.UpiAccount;
import haveno.core.payment.VenmoAccount;
import haveno.core.payment.VerseAccount;
import haveno.core.payment.WeChatPayAccount;
import haveno.core.payment.WesternUnionAccount;
@ -190,14 +193,11 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
public static final String SWIFT_ID = "SWIFT";
public static final String ACH_TRANSFER_ID = "ACH_TRANSFER";
public static final String DOMESTIC_WIRE_TRANSFER_ID = "DOMESTIC_WIRE_TRANSFER";
// Cannot be deleted as it would break old trade history entries
@Deprecated
public static final String OK_PAY_ID = "OK_PAY";
@Deprecated
public static final String CASH_APP_ID = "CASH_APP"; // Removed due too high chargeback risk
@Deprecated
public static final String VENMO_ID = "VENMO"; // Removed due too high chargeback risk
public static final String OK_PAY_ID = "OK_PAY"; // Cannot be deleted as it would break old trade history entries
public static final String CASH_APP_ID = "CASH_APP";
public static final String VENMO_ID = "VENMO";
public static final String PAYPAL_ID = "PAYPAL";
public static PaymentMethod UPHOLD;
public static PaymentMethod MONEY_BEAM;
@ -254,14 +254,13 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
public static PaymentMethod ACH_TRANSFER;
public static PaymentMethod DOMESTIC_WIRE_TRANSFER;
public static PaymentMethod BSQ_SWAP;
public static PaymentMethod PAYPAL;
public static PaymentMethod CASH_APP;
public static PaymentMethod VENMO;
// Cannot be deleted as it would break old trade history entries
@Deprecated
public static PaymentMethod OK_PAY = getDummyPaymentMethod(OK_PAY_ID);
@Deprecated
public static PaymentMethod CASH_APP = getDummyPaymentMethod(CASH_APP_ID); // Removed due too high chargeback risk
@Deprecated
public static PaymentMethod VENMO = getDummyPaymentMethod(VENMO_ID); // Removed due too high chargeback risk
// The limit and duration assignment must not be changed as that could break old offers (if amount would be higher
// than new trade limit) and violate the maker expectation when he created the offer (duration).
@ -280,9 +279,9 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
// US
ZELLE = new PaymentMethod(ZELLE_ID, 4 * DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(ZelleAccount.SUPPORTED_CURRENCIES)),
POPMONEY = new PaymentMethod(POPMONEY_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(PopmoneyAccount.SUPPORTED_CURRENCIES)),
US_POSTAL_MONEY_ORDER = new PaymentMethod(US_POSTAL_MONEY_ORDER_ID, 8 * DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(USPostalMoneyOrderAccount.SUPPORTED_CURRENCIES)),
VENMO = new PaymentMethod(VENMO_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(VenmoAccount.SUPPORTED_CURRENCIES)),
// Canada
INTERAC_E_TRANSFER = new PaymentMethod(INTERAC_E_TRANSFER_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(InteracETransferAccount.SUPPORTED_CURRENCIES)),
@ -326,6 +325,8 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
SWIFT = new PaymentMethod(SWIFT_ID, 7 * DAY, DEFAULT_TRADE_LIMIT_MID_RISK, getAssetCodes(SwiftAccount.SUPPORTED_CURRENCIES)),
ACH_TRANSFER = new PaymentMethod(ACH_TRANSFER_ID, 5 * DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(AchTransferAccount.SUPPORTED_CURRENCIES)),
DOMESTIC_WIRE_TRANSFER = new PaymentMethod(DOMESTIC_WIRE_TRANSFER_ID, 3 * DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(DomesticWireTransferAccount.SUPPORTED_CURRENCIES)),
PAYPAL = new PaymentMethod(PAYPAL_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(PayPalAccount.SUPPORTED_CURRENCIES)),
CASH_APP = new PaymentMethod(CASH_APP_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(CashAppAccount.SUPPORTED_CURRENCIES)),
// Japan
JAPAN_BANK = new PaymentMethod(JAPAN_BANK_ID, DAY, DEFAULT_TRADE_LIMIT_LOW_RISK, getAssetCodes(JapanBankAccount.SUPPORTED_CURRENCIES)),
@ -342,6 +343,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
// Cryptos
BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, DAY, DEFAULT_TRADE_LIMIT_VERY_LOW_RISK, Arrays.asList()),
// Cryptos with 1 hour trade period
BLOCK_CHAINS_INSTANT = new PaymentMethod(BLOCK_CHAINS_INSTANT_ID, TimeUnit.HOURS.toMillis(1), DEFAULT_TRADE_LIMIT_VERY_LOW_RISK, Arrays.asList())
);
@ -364,7 +366,10 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
TRANSFERWISE_ID,
UPHOLD_ID,
ZELLE_ID,
AUSTRALIA_PAYID_ID);
AUSTRALIA_PAYID_ID,
CASH_APP_ID,
PAYPAL_ID,
VENMO_ID);
return paymentMethods.stream().filter(paymentMethod -> paymentMethodIds.contains(paymentMethod.getId())).collect(Collectors.toList());
}
@ -581,7 +586,10 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
id.equals(PaymentMethod.CHASE_QUICK_PAY_ID) ||
id.equals(PaymentMethod.POPMONEY_ID) ||
id.equals(PaymentMethod.MONEY_BEAM_ID) ||
id.equals(PaymentMethod.UPHOLD_ID);
id.equals(PaymentMethod.UPHOLD_ID) ||
id.equals(PaymentMethod.CASH_APP_ID) ||
id.equals(PaymentMethod.PAYPAL_ID) ||
id.equals(PaymentMethod.VENMO_ID);
}
public static boolean isRoundedForAtmCash(String id) {

View file

@ -40,16 +40,16 @@ public final class RevolutAccountPayload extends PaymentAccountPayload {
// Was added in 1.3.8
// To not break signed accounts we keep accountId as internal id used for signing.
// Old accounts get a popup to add the new required field userName but accountId is
// left unchanged. Newly created accounts fill accountId with the value of userName.
// In the UI we only use userName.
// Old accounts get a popup to add the new required field username but accountId is
// left unchanged. Newly created accounts fill accountId with the value of username.
// In the UI we only use username.
// For backward compatibility we need to exclude the new field for the contract json.
// We can remove that after a while when risk that users with pre 1.3.8 version trade with updated
// users is very low.
@JsonExclude
@Getter
private String userName = "";
private String username = "";
public RevolutAccountPayload(String paymentMethod, String id) {
super(paymentMethod, id);
@ -62,7 +62,7 @@ public final class RevolutAccountPayload extends PaymentAccountPayload {
private RevolutAccountPayload(String paymentMethod,
String id,
@Nullable String userName,
@Nullable String username,
long maxTradePeriod,
Map<String, String> excludeFromJsonDataMap) {
super(paymentMethod,
@ -70,13 +70,13 @@ public final class RevolutAccountPayload extends PaymentAccountPayload {
maxTradePeriod,
excludeFromJsonDataMap);
this.userName = userName;
this.username = username;
}
@Override
public Message toProtoMessage() {
protobuf.RevolutAccountPayload.Builder revolutBuilder = protobuf.RevolutAccountPayload.newBuilder()
.setUserName(userName);
.setUsername(username);
return getPaymentAccountPayloadBuilder().setRevolutAccountPayload(revolutBuilder).build();
}
@ -85,7 +85,7 @@ public final class RevolutAccountPayload extends PaymentAccountPayload {
protobuf.RevolutAccountPayload revolutAccountPayload = proto.getRevolutAccountPayload();
return new RevolutAccountPayload(proto.getPaymentMethodId(),
proto.getId(),
revolutAccountPayload.getUserName(),
revolutAccountPayload.getUsername(),
proto.getMaxTradePeriod(),
new HashMap<>(proto.getExcludeFromJsonDataMap()));
}
@ -104,9 +104,9 @@ public final class RevolutAccountPayload extends PaymentAccountPayload {
private Tuple2<String, String> getLabelValueTuple() {
String label;
String value;
checkArgument(!userName.isEmpty(), "Username must be set");
label = Res.get("payment.account.userName");
value = userName;
checkArgument(!username.isEmpty(), "Username must be set");
label = Res.get("payment.account.username");
value = username;
return new Tuple2<>(label, value);
}
@ -123,14 +123,14 @@ public final class RevolutAccountPayload extends PaymentAccountPayload {
@Override
public byte[] getAgeWitnessInputData() {
return super.getAgeWitnessInputData(userName.getBytes(StandardCharsets.UTF_8));
return super.getAgeWitnessInputData(username.getBytes(StandardCharsets.UTF_8));
}
public boolean userNameNotSet() {
return userName.isEmpty();
public boolean usernameNotSet() {
return username.isEmpty();
}
public void setUserName(String userName) {
this.userName = userName;
public void setUserName(String username) {
this.username = username;
}
}

View file

@ -91,7 +91,7 @@ public final class SatispayAccountPayload extends CountryBasedPaymentAccountPayl
@Override
public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.userName") + " " + holderName;
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.username") + " " + holderName;
}
@Override

View file

@ -86,7 +86,7 @@ public final class StrikeAccountPayload extends CountryBasedPaymentAccountPayloa
@Override
public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.userName") + " " + holderName;
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.username") + " " + holderName;
}
@Override

View file

@ -96,7 +96,7 @@ public final class TransferwiseUsdAccountPayload extends CountryBasedPaymentAcco
@Override
public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.userName") + " " + holderName;
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.username") + " " + holderName;
}
@Override

View file

@ -29,17 +29,13 @@ import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
// Cannot be deleted as it would break old trade history entries
// Removed due too high chargeback risk
@Deprecated
@EqualsAndHashCode(callSuper = true)
@ToString
@Setter
@Getter
@Slf4j
public final class VenmoAccountPayload extends PaymentAccountPayload {
private String venmoUserName = "";
private String holderName = "";
private String emailOrMobileNrOrUsername = "";
public VenmoAccountPayload(String paymentMethod, String id) {
super(paymentMethod, id);
@ -52,8 +48,7 @@ public final class VenmoAccountPayload extends PaymentAccountPayload {
private VenmoAccountPayload(String paymentMethod,
String id,
String venmoUserName,
String holderName,
String emailOrMobileNrOrUsername,
long maxTradePeriod,
Map<String, String> excludeFromJsonDataMap) {
super(paymentMethod,
@ -61,24 +56,21 @@ public final class VenmoAccountPayload extends PaymentAccountPayload {
maxTradePeriod,
excludeFromJsonDataMap);
this.venmoUserName = venmoUserName;
this.holderName = holderName;
this.emailOrMobileNrOrUsername = emailOrMobileNrOrUsername;
}
@Override
public Message toProtoMessage() {
return getPaymentAccountPayloadBuilder()
.setVenmoAccountPayload(protobuf.VenmoAccountPayload.newBuilder()
.setVenmoUserName(venmoUserName)
.setHolderName(holderName))
.setEmailOrMobileNrOrUsername(emailOrMobileNrOrUsername))
.build();
}
public static VenmoAccountPayload fromProto(protobuf.PaymentAccountPayload proto) {
return new VenmoAccountPayload(proto.getPaymentMethodId(),
proto.getId(),
proto.getVenmoAccountPayload().getVenmoUserName(),
proto.getVenmoAccountPayload().getHolderName(),
proto.getVenmoAccountPayload().getEmailOrMobileNrOrUsername(),
proto.getMaxTradePeriod(),
new HashMap<>(proto.getExcludeFromJsonDataMap()));
}
@ -90,8 +82,7 @@ public final class VenmoAccountPayload extends PaymentAccountPayload {
@Override
public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.owner") + " " + holderName + ", " +
Res.getWithCol("payment.venmo.venmoUserName") + " " + venmoUserName;
return Res.getWithCol("payment.email.mobile.username") + " " + emailOrMobileNrOrUsername;
}
@Override
@ -101,11 +92,6 @@ public final class VenmoAccountPayload extends PaymentAccountPayload {
@Override
public byte[] getAgeWitnessInputData() {
return super.getAgeWitnessInputData(venmoUserName.getBytes(StandardCharsets.UTF_8));
}
@Override
public String getOwnerId() {
return holderName;
return super.getAgeWitnessInputData(emailOrMobileNrOrUsername.getBytes(StandardCharsets.UTF_8));
}
}

View file

@ -71,7 +71,7 @@ public final class VerseAccountPayload extends PaymentAccountPayload {
@Override
public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.userName") + " " + holderName;
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.username") + " " + holderName;
}
@Override

View file

@ -0,0 +1,56 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package haveno.core.payment.validation;
import haveno.core.util.validation.InputValidator;
public final class EmailOrMobileNrOrCashtagValidator extends InputValidator {
private final EmailOrMobileNrValidator emailOrMobileNrValidator;
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public EmailOrMobileNrOrCashtagValidator() {
emailOrMobileNrValidator = new EmailOrMobileNrValidator();
}
@Override
public ValidationResult validate(String input) {
ValidationResult result = validateIfNotEmpty(input);
if (!result.isValid) {
return result;
} else {
ValidationResult emailOrMobileResult = emailOrMobileNrValidator.validate(input);
if (emailOrMobileResult.isValid)
return emailOrMobileResult;
else
return validateCashtag(input);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
// TODO not impl yet -> see InteracETransferValidator
private ValidationResult validateCashtag(String input) {
return super.validate(input);
}
}

View file

@ -0,0 +1,56 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package haveno.core.payment.validation;
import haveno.core.util.validation.InputValidator;
public final class EmailOrMobileNrOrUsernameValidator extends InputValidator {
private final EmailOrMobileNrValidator emailOrMobileNrValidator;
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public EmailOrMobileNrOrUsernameValidator() {
emailOrMobileNrValidator = new EmailOrMobileNrValidator();
}
@Override
public ValidationResult validate(String input) {
ValidationResult result = validateIfNotEmpty(input);
if (!result.isValid) {
return result;
} else {
ValidationResult emailOrMobileResult = emailOrMobileNrValidator.validate(input);
if (emailOrMobileResult.isValid)
return emailOrMobileResult;
else
return validateName(input);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
// TODO: properly implement username validation
private ValidationResult validateName(String input) {
return super.validate(input);
}
}

View file

@ -19,8 +19,8 @@ package haveno.core.payment.validation;
public final class RevolutValidator extends LengthValidator {
public RevolutValidator() {
// Not sure what are requirements for Revolut user names
// Please keep in mind that even we force users to set user name at startup we should handle also the case
// Not sure what are requirements for Revolut usernames
// Please keep in mind that even we force users to set username at startup we should handle also the case
// that the old accountID as phone number or email is displayed at the username text field and we do not
// want to break validation in those cases. So being too strict on the validators might cause more troubles
// as its worth...

View file

@ -54,6 +54,7 @@ import haveno.core.payment.payload.NequiAccountPayload;
import haveno.core.payment.payload.OKPayAccountPayload;
import haveno.core.payment.payload.PaxumAccountPayload;
import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.payment.payload.PayPalAccountPayload;
import haveno.core.payment.payload.PayseraAccountPayload;
import haveno.core.payment.payload.PaytmAccountPayload;
import haveno.core.payment.payload.PerfectMoneyAccountPayload;
@ -236,6 +237,8 @@ public class CoreProtoResolver implements ProtoResolver {
return CashAppAccountPayload.fromProto(proto);
case VENMO_ACCOUNT_PAYLOAD:
return VenmoAccountPayload.fromProto(proto);
case PAYPAL_ACCOUNT_PAYLOAD:
return PayPalAccountPayload.fromProto(proto);
default:
throw new ProtobufferRuntimeException("Unknown proto message case(PB.PaymentAccountPayload). messageCase=" + messageCase);

View file

@ -45,7 +45,7 @@ public class DisputeAgentLookupMap {
case "6c4cim7h7t3bm4bnchbf727qrhdfrfr6lhod25wjtizm2sifpkktvwad.onion:9999":
return "pazza83";
default:
log.warn("No user name for dispute agent with address {} found.", fullAddress);
log.warn("No username for dispute agent with address {} found.", fullAddress);
return Res.get("shared.na");
}
}

View file

@ -155,7 +155,8 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
TIKKIE,
TRANSFERWISE_USD,
ACH_TRANSFER,
DOMESTIC_WIRE_TRANSFER
DOMESTIC_WIRE_TRANSFER,
PAYPAL
}
@Getter