Skip to content

Commit ce6688a

Browse files
committed
add paysafe payment method
1 parent 71fab72 commit ce6688a

26 files changed

+446
-4
lines changed

core/src/main/java/haveno/core/api/model/PaymentAccountForm.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ public enum FormId {
7777
AUSTRALIA_PAYID,
7878
CASH_APP,
7979
PAYPAL,
80-
VENMO;
80+
VENMO,
81+
PAYSAFE;
8182

8283
public static PaymentAccountForm.FormId fromProto(protobuf.PaymentAccountForm.FormId formId) {
8384
return ProtoUtil.enumFromProto(PaymentAccountForm.FormId.class, formId.name());

core/src/main/java/haveno/core/payment/PaymentAccountFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ public static PaymentAccount getPaymentAccount(PaymentMethod paymentMethod) {
136136
return new CashAppAccount();
137137
case PaymentMethod.VENMO_ID:
138138
return new VenmoAccount();
139+
case PaymentMethod.PAYSAFE_ID:
140+
return new PaysafeAccount();
139141

140142
// Cannot be deleted as it would break old trade history entries
141143
case PaymentMethod.OK_PAY_ID:
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* This file is part of Haveno.
3+
*
4+
* Haveno is free software: you can redistribute it and/or modify it
5+
* under the terms of the GNU Affero General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or (at
7+
* your option) any later version.
8+
*
9+
* Haveno is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12+
* License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero General Public License
15+
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package haveno.core.payment;
19+
20+
import haveno.core.api.model.PaymentAccountFormField;
21+
import haveno.core.locale.TraditionalCurrency;
22+
import haveno.core.locale.TradeCurrency;
23+
import haveno.core.payment.payload.PaymentAccountPayload;
24+
import haveno.core.payment.payload.PaymentMethod;
25+
import haveno.core.payment.payload.PaysafeAccountPayload;
26+
import lombok.EqualsAndHashCode;
27+
import org.jetbrains.annotations.NotNull;
28+
29+
import java.util.List;
30+
31+
@EqualsAndHashCode(callSuper = true)
32+
public final class PaysafeAccount extends PaymentAccount {
33+
34+
private static final List<PaymentAccountFormField.FieldId> INPUT_FIELD_IDS = List.of(
35+
PaymentAccountFormField.FieldId.ACCOUNT_NAME,
36+
PaymentAccountFormField.FieldId.EMAIL,
37+
PaymentAccountFormField.FieldId.TRADE_CURRENCIES,
38+
PaymentAccountFormField.FieldId.SALT
39+
);
40+
41+
// https://developer.paysafe.com/en/support/reference-information/codes/
42+
public static final List<TradeCurrency> SUPPORTED_CURRENCIES = List.of(
43+
new TraditionalCurrency("AED"),
44+
new TraditionalCurrency("AFN"),
45+
new TraditionalCurrency("ARS"),
46+
new TraditionalCurrency("AUD"),
47+
new TraditionalCurrency("AZN"),
48+
new TraditionalCurrency("BAM"),
49+
new TraditionalCurrency("BHD"),
50+
new TraditionalCurrency("BGN"),
51+
new TraditionalCurrency("BOB"),
52+
new TraditionalCurrency("BRL"),
53+
new TraditionalCurrency("CAD"),
54+
new TraditionalCurrency("CHF"),
55+
new TraditionalCurrency("CLP"),
56+
new TraditionalCurrency("COP"),
57+
new TraditionalCurrency("CRC"),
58+
new TraditionalCurrency("CZK"),
59+
new TraditionalCurrency("DKK"),
60+
new TraditionalCurrency("DOP"),
61+
new TraditionalCurrency("EGP"),
62+
new TraditionalCurrency("ETB"),
63+
new TraditionalCurrency("EUR"),
64+
new TraditionalCurrency("FJD"),
65+
new TraditionalCurrency("GBP"),
66+
new TraditionalCurrency("GEL"),
67+
new TraditionalCurrency("GTQ"),
68+
new TraditionalCurrency("HKD"),
69+
new TraditionalCurrency("HRK"),
70+
new TraditionalCurrency("HUF"),
71+
new TraditionalCurrency("ILS"),
72+
new TraditionalCurrency("INR"),
73+
new TraditionalCurrency("IDR"),
74+
new TraditionalCurrency("JPY"),
75+
new TraditionalCurrency("JMD"),
76+
new TraditionalCurrency("JOD"),
77+
new TraditionalCurrency("KES"),
78+
new TraditionalCurrency("KRW"),
79+
new TraditionalCurrency("KWD"),
80+
new TraditionalCurrency("LVL"),
81+
new TraditionalCurrency("LBP"),
82+
new TraditionalCurrency("LYD"),
83+
new TraditionalCurrency("MDL"),
84+
new TraditionalCurrency("MAD"),
85+
new TraditionalCurrency("MWK"),
86+
new TraditionalCurrency("MXN"),
87+
new TraditionalCurrency("MUR"),
88+
new TraditionalCurrency("NGN"),
89+
new TraditionalCurrency("NOK"),
90+
new TraditionalCurrency("NZD"),
91+
new TraditionalCurrency("OMR"),
92+
new TraditionalCurrency("PAB"),
93+
new TraditionalCurrency("PEN"),
94+
new TraditionalCurrency("PHP"),
95+
new TraditionalCurrency("PLN"),
96+
new TraditionalCurrency("PKR"),
97+
new TraditionalCurrency("QAR"),
98+
new TraditionalCurrency("RON"),
99+
new TraditionalCurrency("RSD"),
100+
new TraditionalCurrency("RUB"),
101+
new TraditionalCurrency("SAR"),
102+
new TraditionalCurrency("SEK"),
103+
new TraditionalCurrency("SGD"),
104+
new TraditionalCurrency("SYP"),
105+
new TraditionalCurrency("TND"),
106+
new TraditionalCurrency("THB"),
107+
new TraditionalCurrency("TTD"),
108+
new TraditionalCurrency("TRY"),
109+
new TraditionalCurrency("UAH"),
110+
new TraditionalCurrency("USD"),
111+
new TraditionalCurrency("UYU"),
112+
new TraditionalCurrency("VEF"),
113+
new TraditionalCurrency("VND"),
114+
new TraditionalCurrency("XCD")
115+
);
116+
117+
public PaysafeAccount() {
118+
super(PaymentMethod.PAYSAFE);
119+
}
120+
121+
@Override
122+
protected PaymentAccountPayload createPayload() {
123+
return new PaysafeAccountPayload(paymentMethod.getId(), id);
124+
}
125+
126+
@Override
127+
public @NotNull List<TradeCurrency> getSupportedCurrencies() {
128+
return SUPPORTED_CURRENCIES;
129+
}
130+
131+
@Override
132+
public @NotNull List<PaymentAccountFormField.FieldId> getInputFieldIds() {
133+
return INPUT_FIELD_IDS;
134+
}
135+
136+
public void setEmail(String accountId) {
137+
((PaysafeAccountPayload) paymentAccountPayload).setEmail(accountId);
138+
}
139+
140+
public String getEmail() {
141+
return ((PaysafeAccountPayload) paymentAccountPayload).getEmail();
142+
}
143+
144+
@Override
145+
protected PaymentAccountFormField getEmptyFormField(PaymentAccountFormField.FieldId fieldId) {
146+
var field = super.getEmptyFormField(fieldId);
147+
if (field.getId() == PaymentAccountFormField.FieldId.TRADE_CURRENCIES) field.setValue("");
148+
return field;
149+
}
150+
}

core/src/main/java/haveno/core/payment/payload/PaymentMethod.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import haveno.core.payment.CashAtAtmAccount;
5252
import haveno.core.payment.PayByMailAccount;
5353
import haveno.core.payment.PayPalAccount;
54+
import haveno.core.payment.PaysafeAccount;
5455
import haveno.core.payment.CashDepositAccount;
5556
import haveno.core.payment.CelPayAccount;
5657
import haveno.core.payment.ZelleAccount;
@@ -193,6 +194,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
193194
public static final String CASH_APP_ID = "CASH_APP";
194195
public static final String VENMO_ID = "VENMO";
195196
public static final String PAYPAL_ID = "PAYPAL";
197+
public static final String PAYSAFE_ID = "PAYSAFE";
196198

197199
public static PaymentMethod UPHOLD;
198200
public static PaymentMethod MONEY_BEAM;
@@ -252,6 +254,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
252254
public static PaymentMethod PAYPAL;
253255
public static PaymentMethod CASH_APP;
254256
public static PaymentMethod VENMO;
257+
public static PaymentMethod PAYSAFE;
255258

256259
// Cannot be deleted as it would break old trade history entries
257260
@Deprecated
@@ -322,6 +325,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
322325
DOMESTIC_WIRE_TRANSFER = new PaymentMethod(DOMESTIC_WIRE_TRANSFER_ID, 3 * DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(DomesticWireTransferAccount.SUPPORTED_CURRENCIES)),
323326
PAYPAL = new PaymentMethod(PAYPAL_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(PayPalAccount.SUPPORTED_CURRENCIES)),
324327
CASH_APP = new PaymentMethod(CASH_APP_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(CashAppAccount.SUPPORTED_CURRENCIES)),
328+
PAYSAFE = new PaymentMethod(PaymentMethod.PAYSAFE_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(PaysafeAccount.SUPPORTED_CURRENCIES)),
325329

326330
// Japan
327331
JAPAN_BANK = new PaymentMethod(JAPAN_BANK_ID, DAY, DEFAULT_TRADE_LIMIT_LOW_RISK, getAssetCodes(JapanBankAccount.SUPPORTED_CURRENCIES)),
@@ -364,7 +368,8 @@ public static List<PaymentMethod> getPaymentMethods() {
364368
AUSTRALIA_PAYID_ID,
365369
CASH_APP_ID,
366370
PAYPAL_ID,
367-
VENMO_ID);
371+
VENMO_ID,
372+
PAYSAFE_ID);
368373
return paymentMethods.stream().filter(paymentMethod -> paymentMethodIds.contains(paymentMethod.getId())).collect(Collectors.toList());
369374
}
370375

@@ -588,7 +593,8 @@ public static boolean hasChargebackRisk(String id, String currencyCode) {
588593
id.equals(PaymentMethod.UPHOLD_ID) ||
589594
id.equals(PaymentMethod.CASH_APP_ID) ||
590595
id.equals(PaymentMethod.PAYPAL_ID) ||
591-
id.equals(PaymentMethod.VENMO_ID);
596+
id.equals(PaymentMethod.VENMO_ID) ||
597+
id.equals(PaymentMethod.PAYSAFE_ID);
592598
}
593599

594600
public static boolean isRoundedForAtmCash(String id) {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* This file is part of Haveno.
3+
*
4+
* Haveno is free software: you can redistribute it and/or modify it
5+
* under the terms of the GNU Affero General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or (at
7+
* your option) any later version.
8+
*
9+
* Haveno is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12+
* License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero General Public License
15+
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package haveno.core.payment.payload;
19+
20+
import com.google.protobuf.Message;
21+
import haveno.core.locale.Res;
22+
import lombok.EqualsAndHashCode;
23+
import lombok.Getter;
24+
import lombok.Setter;
25+
import lombok.ToString;
26+
import lombok.extern.slf4j.Slf4j;
27+
28+
import java.nio.charset.StandardCharsets;
29+
import java.util.HashMap;
30+
import java.util.Map;
31+
32+
@EqualsAndHashCode(callSuper = true)
33+
@ToString
34+
@Setter
35+
@Getter
36+
@Slf4j
37+
public final class PaysafeAccountPayload extends PaymentAccountPayload {
38+
private String email = "";
39+
40+
public PaysafeAccountPayload(String paymentMethod, String id) {
41+
super(paymentMethod, id);
42+
}
43+
44+
45+
///////////////////////////////////////////////////////////////////////////////////////////
46+
// PROTO BUFFER
47+
///////////////////////////////////////////////////////////////////////////////////////////
48+
49+
private PaysafeAccountPayload(String paymentMethod,
50+
String id,
51+
String email,
52+
long maxTradePeriod,
53+
Map<String, String> excludeFromJsonDataMap) {
54+
super(paymentMethod,
55+
id,
56+
maxTradePeriod,
57+
excludeFromJsonDataMap);
58+
59+
this.email = email;
60+
}
61+
62+
@Override
63+
public Message toProtoMessage() {
64+
return getPaymentAccountPayloadBuilder()
65+
.setPaysafeAccountPayload(protobuf.PaysafeAccountPayload.newBuilder().setEmail(email))
66+
.build();
67+
}
68+
69+
public static PaysafeAccountPayload fromProto(protobuf.PaymentAccountPayload proto) {
70+
return new PaysafeAccountPayload(proto.getPaymentMethodId(),
71+
proto.getId(),
72+
proto.getPaysafeAccountPayload().getEmail(),
73+
proto.getMaxTradePeriod(),
74+
new HashMap<>(proto.getExcludeFromJsonDataMap()));
75+
}
76+
77+
78+
///////////////////////////////////////////////////////////////////////////////////////////
79+
// API
80+
///////////////////////////////////////////////////////////////////////////////////////////
81+
82+
@Override
83+
public String getPaymentDetails() {
84+
return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.email") + " " + email;
85+
}
86+
87+
@Override
88+
public String getPaymentDetailsForTradePopup() {
89+
return getPaymentDetails();
90+
}
91+
92+
@Override
93+
public byte[] getAgeWitnessInputData() {
94+
return super.getAgeWitnessInputData(email.getBytes(StandardCharsets.UTF_8));
95+
}
96+
}

core/src/main/java/haveno/core/proto/CoreProtoResolver.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import haveno.core.payment.payload.OKPayAccountPayload;
5555
import haveno.core.payment.payload.PaxumAccountPayload;
5656
import haveno.core.payment.payload.PaymentAccountPayload;
57+
import haveno.core.payment.payload.PaysafeAccountPayload;
5758
import haveno.core.payment.payload.PayPalAccountPayload;
5859
import haveno.core.payment.payload.PayseraAccountPayload;
5960
import haveno.core.payment.payload.PaytmAccountPayload;
@@ -239,6 +240,8 @@ public PaymentAccountPayload fromProto(protobuf.PaymentAccountPayload proto) {
239240
return VenmoAccountPayload.fromProto(proto);
240241
case PAYPAL_ACCOUNT_PAYLOAD:
241242
return PayPalAccountPayload.fromProto(proto);
243+
case PAYSAFE_ACCOUNT_PAYLOAD:
244+
return PaysafeAccountPayload.fromProto(proto);
242245

243246
default:
244247
throw new ProtobufferRuntimeException("Unknown proto message case(PB.PaymentAccountPayload). messageCase=" + messageCase);

core/src/main/resources/i18n/displayStrings.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3067,6 +3067,9 @@ payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send
30673067
- 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 \
30683068
to tell your trading peer the reference text you picked so they can verify your payment)\n\
30693069
- 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)
3070+
payment.paysafe.info=For your protection, we strongly discourage using Paysafecard PINs for payment.\n\n\
3071+
Transactions made via PINs cannot be independently verified for dispute resolution. If an issue arises, recovering funds may not be possible.\n\n\
3072+
To ensure transaction security with dispute resolution, always use payment methods that provide verifiable records.
30703073

30713074
# We use constants from the code so we do not use our normal naming convention
30723075
# dynamic values are not recognized by IntelliJ
@@ -3302,6 +3305,8 @@ CASH_APP_SHORT=Cash App
33023305
# suppress inspection "UnusedProperty"
33033306
VENMO_SHORT=Venmo
33043307
PAYPAL_SHORT=PayPal
3308+
# suppress inspection "UnusedProperty"
3309+
PAYSAFE=Paysafe
33053310

33063311

33073312
####################################################################

core/src/main/resources/i18n/displayStrings_cs.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,6 +3065,10 @@ payment.amazonGiftCard.info=Chcete-li platit dárkovou kartou Amazon eGift, bude
30653065
- 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ě \
30663066
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\
30673067
- 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).
3068+
payment.paysafe.info=Pro vaši ochranu důrazně nedoporučujeme používat Paysafecard PINy pro platby.\n\n\
3069+
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\
3070+
Pro zajištění bezpečnosti transakcí a podpory řešení sporů vždy používejte platební metody, které poskytují ověřitelné záznamy.
3071+
30683072

30693073
# We use constants from the code so we do not use our normal naming convention
30703074
# dynamic values are not recognized by IntelliJ

core/src/main/resources/i18n/displayStrings_de.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,6 +2047,9 @@ payment.australia.payid=PayID
20472047
payment.payid=PayIDs wie E-Mail Adressen oder Telefonnummern die mit Finanzinstitutionen verbunden sind.
20482048
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/]
20492049
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)
2050+
payment.paysafe.info=Zum Schutz Ihrer Sicherheit raten wir dringend davon ab, Paysafecard-PINs für Zahlungen zu verwenden.\n\n\
2051+
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\
2052+
Um die Transaktionssicherheit mit Streitbeilegung zu gewährleisten, verwenden Sie immer Zahlungsmethoden, die überprüfbare Aufzeichnungen bieten.
20502053

20512054

20522055
# We use constants from the code so we do not use our normal naming convention

0 commit comments

Comments
 (0)