Skip to content

Commit

Permalink
add paysafe payment method
Browse files Browse the repository at this point in the history
  • Loading branch information
woodser committed Feb 2, 2025
1 parent 71fab72 commit 633e290
Show file tree
Hide file tree
Showing 26 changed files with 409 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public enum FormId {
AUSTRALIA_PAYID,
CASH_APP,
PAYPAL,
VENMO;
VENMO,
PAYSAFE;

public static PaymentAccountForm.FormId fromProto(protobuf.PaymentAccountForm.FormId formId) {
return ProtoUtil.enumFromProto(PaymentAccountForm.FormId.class, formId.name());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ public static PaymentAccount getPaymentAccount(PaymentMethod paymentMethod) {
return new CashAppAccount();
case PaymentMethod.VENMO_ID:
return new VenmoAccount();
case PaymentMethod.PAYSAFE_ID:
return new PaysafeAccount();

// Cannot be deleted as it would break old trade history entries
case PaymentMethod.OK_PAY_ID:
Expand Down
113 changes: 113 additions & 0 deletions core/src/main/java/haveno/core/payment/PaysafeAccount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* 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.PaysafeAccountPayload;
import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;

import java.util.List;

@EqualsAndHashCode(callSuper = true)
public final class PaysafeAccount extends PaymentAccount {

private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
PaymentAccountFormField.FieldId.EMAIL,
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
PaymentAccountFormField.FieldId.SALT
);

// https://developer.paysafe.com/en/support/reference-information/codes/
public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(
new TraditionalCurrency("AED"),
new TraditionalCurrency("ARS"),
new TraditionalCurrency("AUD"),
new TraditionalCurrency("BGN"),
new TraditionalCurrency("BRL"),
new TraditionalCurrency("CAD"),
new TraditionalCurrency("CHF"),
new TraditionalCurrency("CZK"),
new TraditionalCurrency("DKK"),
new TraditionalCurrency("EGP"),
new TraditionalCurrency("EUR"),
new TraditionalCurrency("GBP"),
new TraditionalCurrency("GEL"),
new TraditionalCurrency("HRK"),
new TraditionalCurrency("HUF"),
new TraditionalCurrency("ILS"),
new TraditionalCurrency("INR"),
new TraditionalCurrency("JPY"),
new TraditionalCurrency("ISK"),
new TraditionalCurrency("KWD"),
new TraditionalCurrency("KRW"),
new TraditionalCurrency("MXN"),
new TraditionalCurrency("NOK"),
new TraditionalCurrency("NZD"),
new TraditionalCurrency("PEN"),
new TraditionalCurrency("PHP"),
new TraditionalCurrency("PLN"),
new TraditionalCurrency("RON"),
new TraditionalCurrency("RSD"),
new TraditionalCurrency("RUB"),
new TraditionalCurrency("SAR"),
new TraditionalCurrency("SEK"),
new TraditionalCurrency("TRY"),
new TraditionalCurrency("USD"),
new TraditionalCurrency("UYU")
);

public PaysafeAccount() {
super(PaymentMethod.PAYSAFE);
}

@Override
protected PaymentAccountPayload createPayload() {
return new PaysafeAccountPayload(paymentMethod.getId(), id);
}

@Override
public @NotNull List<TradeCurrency> getSupportedCurrencies() {
return SUPPORTED_CURRENCIES;
}

@Override
public @NotNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
return INPUT_FIELD_IDS;
}

public void setEmail(String accountId) {
((PaysafeAccountPayload) paymentAccountPayload).setEmail(accountId);
}

public String getEmail() {
return ((PaysafeAccountPayload) paymentAccountPayload).getEmail();
}

@Override
protected PaymentAccountFormField getEmptyFormField(PaymentAccountFormField.FieldId fieldId) {
var field = super.getEmptyFormField(fieldId);
if (field.getId() == PaymentAccountFormField.FieldId.TRADE_CURRENCIES) field.setValue("");
return field;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import haveno.core.payment.CashAtAtmAccount;
import haveno.core.payment.PayByMailAccount;
import haveno.core.payment.PayPalAccount;
import haveno.core.payment.PaysafeAccount;
import haveno.core.payment.CashDepositAccount;
import haveno.core.payment.CelPayAccount;
import haveno.core.payment.ZelleAccount;
Expand Down Expand Up @@ -193,6 +194,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
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 final String PAYSAFE_ID = "PAYSAFE";

public static PaymentMethod UPHOLD;
public static PaymentMethod MONEY_BEAM;
Expand Down Expand Up @@ -252,6 +254,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
public static PaymentMethod PAYPAL;
public static PaymentMethod CASH_APP;
public static PaymentMethod VENMO;
public static PaymentMethod PAYSAFE;

// Cannot be deleted as it would break old trade history entries
@Deprecated
Expand Down Expand Up @@ -322,6 +325,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
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)),
PAYSAFE = new PaymentMethod(PaymentMethod.PAYSAFE_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(PaysafeAccount.SUPPORTED_CURRENCIES)),

// Japan
JAPAN_BANK = new PaymentMethod(JAPAN_BANK_ID, DAY, DEFAULT_TRADE_LIMIT_LOW_RISK, getAssetCodes(JapanBankAccount.SUPPORTED_CURRENCIES)),
Expand Down Expand Up @@ -364,7 +368,8 @@ public static List<PaymentMethod> getPaymentMethods() {
AUSTRALIA_PAYID_ID,
CASH_APP_ID,
PAYPAL_ID,
VENMO_ID);
VENMO_ID,
PAYSAFE_ID);
return paymentMethods.stream().filter(paymentMethod -> paymentMethodIds.contains(paymentMethod.getId())).collect(Collectors.toList());
}

Expand Down Expand Up @@ -588,7 +593,8 @@ public static boolean hasChargebackRisk(String id, String currencyCode) {
id.equals(PaymentMethod.UPHOLD_ID) ||
id.equals(PaymentMethod.CASH_APP_ID) ||
id.equals(PaymentMethod.PAYPAL_ID) ||
id.equals(PaymentMethod.VENMO_ID);
id.equals(PaymentMethod.VENMO_ID) ||
id.equals(PaymentMethod.PAYSAFE_ID);
}

public static boolean isRoundedForAtmCash(String id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 PaysafeAccountPayload extends PaymentAccountPayload {
private String email = "";

public PaysafeAccountPayload(String paymentMethod, String id) {
super(paymentMethod, id);
}


///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////

private PaysafeAccountPayload(String paymentMethod,
String id,
String email,
long maxTradePeriod,
Map<String, String> excludeFromJsonDataMap) {
super(paymentMethod,
id,
maxTradePeriod,
excludeFromJsonDataMap);

this.email = email;
}

@Override
public Message toProtoMessage() {
return getPaymentAccountPayloadBuilder()
.setPaysafeAccountPayload(protobuf.PaysafeAccountPayload.newBuilder().setEmail(email))
.build();
}

public static PaysafeAccountPayload fromProto(protobuf.PaymentAccountPayload proto) {
return new PaysafeAccountPayload(proto.getPaymentMethodId(),
proto.getId(),
proto.getPaysafeAccountPayload().getEmail(),
proto.getMaxTradePeriod(),
new HashMap<>(proto.getExcludeFromJsonDataMap()));
}


///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////

@Override
public String getPaymentDetails() {
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.email") + " " + email;
}

@Override
public String getPaymentDetailsForTradePopup() {
return getPaymentDetails();
}

@Override
public byte[] getAgeWitnessInputData() {
return super.getAgeWitnessInputData(email.getBytes(StandardCharsets.UTF_8));
}
}
3 changes: 3 additions & 0 deletions core/src/main/java/haveno/core/proto/CoreProtoResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import haveno.core.payment.payload.OKPayAccountPayload;
import haveno.core.payment.payload.PaxumAccountPayload;
import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.payment.payload.PaysafeAccountPayload;
import haveno.core.payment.payload.PayPalAccountPayload;
import haveno.core.payment.payload.PayseraAccountPayload;
import haveno.core.payment.payload.PaytmAccountPayload;
Expand Down Expand Up @@ -239,6 +240,8 @@ public PaymentAccountPayload fromProto(protobuf.PaymentAccountPayload proto) {
return VenmoAccountPayload.fromProto(proto);
case PAYPAL_ACCOUNT_PAYLOAD:
return PayPalAccountPayload.fromProto(proto);
case PAYSAFE_ACCOUNT_PAYLOAD:
return PaysafeAccountPayload.fromProto(proto);

default:
throw new ProtobufferRuntimeException("Unknown proto message case(PB.PaymentAccountPayload). messageCase=" + messageCase);
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3067,6 +3067,9 @@ payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send
- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat \
to tell your trading peer the reference text you picked so they can verify your payment)\n\
- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it)
payment.paysafe.info=For your protection, we strongly discourage using Paysafecard PINs for payment.\n\n\
Transactions made via PINs cannot be independently verified for dispute resolution. If an issue arises, recovering funds may not be possible.\n\n\
To ensure transaction security with dispute resolution, always use payment methods that provide verifiable records.

# We use constants from the code so we do not use our normal naming convention
# dynamic values are not recognized by IntelliJ
Expand Down Expand Up @@ -3302,6 +3305,8 @@ CASH_APP_SHORT=Cash App
# suppress inspection "UnusedProperty"
VENMO_SHORT=Venmo
PAYPAL_SHORT=PayPal
# suppress inspection "UnusedProperty"
PAYSAFE=Paysafe


####################################################################
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/resources/i18n/displayStrings_cs.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3065,6 +3065,10 @@ payment.amazonGiftCard.info=Chcete-li platit dárkovou kartou Amazon eGift, bude
- Na kartě do zprávy pro příjemce můžete přidat i vlastní originální text (např. "Happy birthday Susan!") spolu s ID obchodu (v takovém případě \
o tom informujte protistranu pomocí obchodovacího chatu, aby mohli s jistotou ověřit, že obdržená dárková karta pochází od vás.)\n\
- Karty Amazon eGift lze uplatnit pouze na té stránce Amazon, na které byly také koupeny (např. karta koupená na amazon.it může být uplatněna zase jen na amazon.it).
payment.paysafe.info=Pro vaši ochranu důrazně nedoporučujeme používat Paysafecard PINy pro platby.\n\n\
Transakce provedené pomocí PINů nelze nezávisle ověřit pro řešení sporů. Pokud nastane problém, obnova prostředků nemusí být možná.\n\n\
Pro zajištění bezpečnosti transakcí a podpory řešení sporů vždy používejte platební metody, které poskytují ověřitelné záznamy.


# We use constants from the code so we do not use our normal naming convention
# dynamic values are not recognized by IntelliJ
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/resources/i18n/displayStrings_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2047,6 +2047,9 @@ payment.australia.payid=PayID
payment.payid=PayIDs wie E-Mail Adressen oder Telefonnummern die mit Finanzinstitutionen verbunden sind.
payment.payid.info=Eine PayID wie eine Telefonnummer, E-Mail Adresse oder Australische Business Number (ABN) mit der Sie sicher Ihre Bank, Kreditgenossenschaft oder Bausparkassenkonto verlinken können. Sie müssen bereits eine PayID mit Ihrer Australischen Finanzinstitution erstellt haben. Beide Institutionen, die die sendet und die die empfängt, müssen PayID unterstützen. Weitere informationen finden Sie unter [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=Um mit einer Amazon eGift Geschenkkarte zu bezahlen, müssen Sie eine Amazon eGift Geschenkkarte über Ihr Amazon-Konto an den XMR-Verkäufer senden. \n\nHaveno zeigt die E-Mail-Adresse oder Telefonnummer des XMR-Verkäufers an, an die die Geschenkkarte gesendet werden soll, und Sie müssen die Handels-ID in das Nachrichtenfeld der Geschenkkarte eintragen. Bitte lesen Sie das Wiki [HYPERLINK:https://haveno.exchange/wiki/Amazon_eGift_card] für weitere Details und empfohlene Vorgehensweisen. \n\nDrei wichtige Hinweise:\n- Versuchen Sie Geschenkkarten mit Beträgen von 100 USD oder weniger zu versenden, weil Amazon größere Geschenkkarten gerne als betrügerisch kennzeichnet\n- Versuchen Sie einen kreativen, glaubwürdigen Text für die Nachricht der Geschenkkarten zu verwenden (z.B. "Alles Gute zum Geburtstag Susi!"), zusammen mit der Handels-ID (und verwenden Sie den Handels-Chat, um Ihrem Handelspartner den von Ihnen gewählten Referenztext mitzuteilen, damit er Ihre Zahlung überprüfen kann)\n- Amazon Geschenkkarten können nur auf der Amazon-Website eingelöst werden, auf der sie gekauft wurden (z. B. kann eine auf amazon.it gekaufte Geschenkkarte nur auf amazon.it eingelöst werden)
payment.paysafe.info=Zum Schutz Ihrer Sicherheit raten wir dringend davon ab, Paysafecard-PINs für Zahlungen zu verwenden.\n\n\
Transaktionen, die über PINs durchgeführt werden, können nicht unabhängig zur Streitbeilegung überprüft werden. Wenn ein Problem auftritt, kann die Rückerstattung von Geldern möglicherweise nicht möglich sein.\n\n\
Um die Transaktionssicherheit mit Streitbeilegung zu gewährleisten, verwenden Sie immer Zahlungsmethoden, die überprüfbare Aufzeichnungen bieten.


# We use constants from the code so we do not use our normal naming convention
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/resources/i18n/displayStrings_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2048,6 +2048,9 @@ payment.australia.payid=PayID
payment.payid=PayID conectado a una institución financiera. Como la dirección email o el número de móvil.
payment.payid.info=Un PayID como un número de teléfono, dirección email o Australian Business Number (ABN), que puede conectar con seguridad a su banco, unión de crédito o cuenta de construcción de sociedad. Necesita haber creado una PayID con su institución financiera australiana. Tanto para enviar y recibir las instituciones financieras deben soportar PayID. Para más información por favor compruebe [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=Para pagar con Tarjeta eGift Amazon. necesitará enviar una Tarjeta eGift Amazon al vendedor XMR a través de su cuenta Amazon.\n\nHaveno mostrará la dirección e-mail del vendedor de XMR o el número de teléfono donde la tarjeta de regalo deberá enviarse. Por favor vea la wiki [HYPERLINK:https://haveno.exchange/wiki/Amazon_eGift_card] para más detalles y mejores prácticas.\n\nNotas importantes:\n- Pruebe a enviar las tarjetas regalo en cantidades de 100USD o menores, ya que Amazon está señalando tarjetas regalo mayores como fraudulentas.\n- Intente usar textos para el mensaje de la tarjeta regalo creíbles y creativos ("Feliz cumpleaños!").\n- Las tarjetas Amazon eGift pueden ser redimidas únicamente en la web de Amazon en la que se compraron (por ejemplo, una tarjeta comprada en amazon.it solo puede ser redimida en amazon.it)
payment.paysafe.info=Por su protección, desaconsejamos encarecidamente el uso de PINs de Paysafecard para pagos.\n\n\
Las transacciones realizadas mediante PINs no pueden ser verificadas de forma independiente para la resolución de disputas. Si surge un problema, recuperar los fondos puede no ser posible.\n\n\
Para garantizar la seguridad de las transacciones con resolución de disputas, utilice siempre métodos de pago que proporcionen registros verificables.


# We use constants from the code so we do not use our normal naming convention
Expand Down
Loading

0 comments on commit 633e290

Please sign in to comment.