mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Delay payout if buyers account got flagged as risky by filter data
Signed-off-by: HenrikJannsen <boilingfrog@gmx.com>
This commit is contained in:
parent
3a25eef64a
commit
01bd8bb86e
@ -912,6 +912,7 @@ portfolio.pending.step3_seller.xmrTxHash=Transaction ID
|
||||
portfolio.pending.step3_seller.xmrTxKey=Transaction key
|
||||
portfolio.pending.step3_seller.buyersAccount=Buyers account data
|
||||
portfolio.pending.step3_seller.confirmReceipt=Confirm payment receipt
|
||||
portfolio.pending.step3_seller.releaseBitcoin=Release Bitcoin
|
||||
portfolio.pending.step3_seller.showBsqWallet=Show payment in BSQ wallet
|
||||
portfolio.pending.step3_seller.buyerStartedPayment=The BTC buyer has started the {0} payment.\n{1}
|
||||
portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Check for blockchain confirmations at your altcoin wallet or block explorer and confirm the payment when you have sufficient blockchain confirmations.
|
||||
@ -922,6 +923,12 @@ portfolio.pending.step3_seller.warn.part2=You still have not confirmed the recei
|
||||
Please check {0} if you have received the payment.
|
||||
portfolio.pending.step3_seller.openForDispute=You have not confirmed the receipt of the payment!\n\
|
||||
The max. period for the trade has elapsed.\nPlease confirm or request assistance from the mediator.
|
||||
|
||||
portfolio.pending.step3_seller.delayedPayout=Trade with ID ''{0}'' has been flagged as high risk.\n\n\
|
||||
You need to wait releasing the Bitcoin to the buyer until {1} to reduce chargeback risks.\n\n\
|
||||
Please note, that this does not mean that the buyer''s account got flagged but maybe their bank or the payment method was used for chargeback fraud in the past.\n\n\
|
||||
You can also request mediation and tell the mediator that this trade got flagged as high risk. The mediator can try to verify the account ownership of the buyer and if nothing is suspicious suggest an earlier payout.
|
||||
|
||||
# suppress inspection "TrailingSpacesInProperty"
|
||||
portfolio.pending.step3_seller.onPaymentReceived.part1=Have you received the {0} payment from your trading partner?\n\n
|
||||
# suppress inspection "TrailingSpacesInProperty"
|
||||
@ -3033,6 +3040,7 @@ filterWindow.offers=Filtered offers (comma sep.)
|
||||
filterWindow.onions=Banned from trading addresses (comma sep.)
|
||||
filterWindow.bannedFromNetwork=Banned from network addresses (comma sep.)
|
||||
filterWindow.accounts=Filtered trading account data:\nFormat: comma sep. list of [payment method id | data field | value]
|
||||
filterWindow.delayedPayout=Require delayed payout:\nFormat: comma sep. list of [payment method id | data field | value]
|
||||
filterWindow.bannedCurrencies=Filtered currency codes (comma sep.)
|
||||
filterWindow.bannedPaymentMethods=Filtered payment method IDs (comma sep.)
|
||||
filterWindow.bannedAccountWitnessSignerPubKeys=Filtered account witness signer pub keys (comma sep. hex of pub keys)
|
||||
|
@ -32,6 +32,7 @@ import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.filter.FilterManager;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OfferDirection;
|
||||
@ -62,6 +63,7 @@ import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.app.DevEnv;
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
import bisq.common.handlers.FaultHandler;
|
||||
@ -88,6 +90,8 @@ import javafx.collections.ObservableList;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -113,6 +117,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
public final WalletPasswordWindow walletPasswordWindow;
|
||||
private final NotificationCenter notificationCenter;
|
||||
private final OfferUtil offerUtil;
|
||||
private final FilterManager filterManager;
|
||||
private final CoinFormatter btcFormatter;
|
||||
|
||||
final ObservableList<PendingTradesListItem> list = FXCollections.observableArrayList();
|
||||
@ -151,6 +156,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
NotificationCenter notificationCenter,
|
||||
OfferUtil offerUtil,
|
||||
FilterManager filterManager,
|
||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter) {
|
||||
this.tradeManager = tradeManager;
|
||||
this.btcWalletService = btcWalletService;
|
||||
@ -167,6 +173,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.notificationCenter = notificationCenter;
|
||||
this.offerUtil = offerUtil;
|
||||
this.filterManager = filterManager;
|
||||
this.btcFormatter = formatter;
|
||||
|
||||
tradesListChangeListener = change -> onListChanged();
|
||||
@ -695,5 +702,33 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean requiresPayoutDelay() {
|
||||
return filterManager.isDelayedPayoutPaymentAccount(Objects.requireNonNull(getTrade()).getProcessModel().getTradePeer().getPaymentAccountPayload());
|
||||
}
|
||||
|
||||
public boolean requiredPayoutDelayHasPassed() {
|
||||
return getSellerConfirmedPaymentReceiptDate() > 0 && new Date().after(getDelayedPayoutDate());
|
||||
}
|
||||
|
||||
public void setSellerConfirmedPaymentReceiptDate() {
|
||||
if (getSellerConfirmedPaymentReceiptDate() == 0) {
|
||||
Objects.requireNonNull(getTrade()).setSellerConfirmedPaymentReceiptDate(new Date().getTime());
|
||||
tradeManager.requestPersistence();
|
||||
}
|
||||
}
|
||||
|
||||
public long getSellerConfirmedPaymentReceiptDate() {
|
||||
return Objects.requireNonNull(getTrade()).getSellerConfirmedPaymentReceiptDate();
|
||||
}
|
||||
|
||||
public Date getDelayedPayoutDate() {
|
||||
return new Date(Objects.requireNonNull(getTrade()).getSellerConfirmedPaymentReceiptDate() + getPayoutDelay());
|
||||
}
|
||||
|
||||
private long getPayoutDelay() {
|
||||
return DevEnv.isDevMode() ? TimeUnit.SECONDS.toMillis(10) :
|
||||
TimeUnit.DAYS.toMillis(14);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import bisq.desktop.main.dao.wallet.tx.BsqTxView;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel;
|
||||
import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
@ -74,7 +75,9 @@ import org.fxmisc.easybind.Subscription;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ -98,6 +101,8 @@ public class SellerStep3View extends TradeStepView {
|
||||
private TxConfidenceIndicator assetTxConfidenceIndicator;
|
||||
@Nullable
|
||||
private ChangeListener<Number> proofResultListener;
|
||||
@Nullable
|
||||
private Timer payoutDelayTimer;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -169,6 +174,8 @@ public class SellerStep3View extends TradeStepView {
|
||||
|
||||
applyAssetTxProofResult(trade.getAssetTxProofResult());
|
||||
}
|
||||
|
||||
updateConfirmButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -189,6 +196,11 @@ public class SellerStep3View extends TradeStepView {
|
||||
if (isXmrTrade()) {
|
||||
trade.getAssetTxProofResultUpdateProperty().removeListener(proofResultListener);
|
||||
}
|
||||
|
||||
if (payoutDelayTimer != null) {
|
||||
payoutDelayTimer.stop();
|
||||
payoutDelayTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -363,7 +375,7 @@ public class SellerStep3View extends TradeStepView {
|
||||
protected void updateDisputeState(Trade.DisputeState disputeState) {
|
||||
super.updateDisputeState(disputeState);
|
||||
|
||||
confirmButton.setDisable(!trade.confirmPermitted());
|
||||
updateConfirmButtonDisableState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -475,14 +487,60 @@ public class SellerStep3View extends TradeStepView {
|
||||
|
||||
private void confirmPaymentReceived() {
|
||||
log.info("User pressed the [Confirm payment receipt] button for Trade {}", trade.getShortId());
|
||||
busyAnimation.play();
|
||||
statusLabel.setText(Res.get("shared.sendingConfirmation"));
|
||||
|
||||
model.dataModel.onFiatPaymentReceived(() -> {
|
||||
}, errorMessage -> {
|
||||
busyAnimation.stop();
|
||||
new Popup().warning(Res.get("popup.warning.sendMsgFailed")).show();
|
||||
});
|
||||
model.dataModel.setSellerConfirmedPaymentReceiptDate();
|
||||
updateConfirmButton();
|
||||
if (!model.dataModel.requiresPayoutDelay() || model.dataModel.requiredPayoutDelayHasPassed()) {
|
||||
onReleaseBitcoin();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateConfirmButton() {
|
||||
if (model.dataModel.requiresPayoutDelay() &&
|
||||
model.dataModel.getSellerConfirmedPaymentReceiptDate() > 0) {
|
||||
if (payoutDelayTimer == null) {
|
||||
confirmButton.setText(Res.get("portfolio.pending.step3_seller.releaseBitcoin"));
|
||||
confirmButton.setOnAction(e -> onReleaseBitcoin());
|
||||
payoutDelayTimer = UserThread.runPeriodically(this::updateConfirmButtonDisableState, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
if (!model.dataModel.requiredPayoutDelayHasPassed()) {
|
||||
new Popup().warning(Res.get("portfolio.pending.step3_seller.delayedPayout",
|
||||
Objects.requireNonNull(model.dataModel.getTrade()).getShortId(),
|
||||
DisplayUtils.formatDateTime(model.dataModel.getDelayedPayoutDate())))
|
||||
.closeButtonText(Res.get("shared.iUnderstand"))
|
||||
.show();
|
||||
}
|
||||
} else {
|
||||
if (payoutDelayTimer != null) {
|
||||
payoutDelayTimer.stop();
|
||||
payoutDelayTimer = null;
|
||||
}
|
||||
confirmButton.setText(Res.get("portfolio.pending.step3_seller.confirmReceipt"));
|
||||
confirmButton.setOnAction(e -> onPaymentReceived());
|
||||
}
|
||||
updateConfirmButtonDisableState();
|
||||
}
|
||||
|
||||
private void updateConfirmButtonDisableState() {
|
||||
boolean confirmedReceiptAndRequiredDelayNotPassed = model.dataModel.getSellerConfirmedPaymentReceiptDate() > 0 &&
|
||||
model.dataModel.requiresPayoutDelay() &&
|
||||
!model.dataModel.requiredPayoutDelayHasPassed();
|
||||
confirmButton.setDisable(!trade.confirmPermitted() || confirmedReceiptAndRequiredDelayNotPassed);
|
||||
}
|
||||
|
||||
private void onReleaseBitcoin() {
|
||||
if (!model.dataModel.requiresPayoutDelay() || model.dataModel.requiredPayoutDelayHasPassed()) {
|
||||
busyAnimation.play();
|
||||
statusLabel.setText(Res.get("shared.sendingConfirmation"));
|
||||
|
||||
model.dataModel.onFiatPaymentReceived(() -> {
|
||||
}, errorMessage -> {
|
||||
busyAnimation.stop();
|
||||
new Popup().warning(Res.get("popup.warning.sendMsgFailed")).show();
|
||||
});
|
||||
} else {
|
||||
log.error("Release Bitcoin is only permitted if no delayed payout is required or delay has passed");
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<String> getOptionalHolderName() {
|
||||
|
Loading…
Reference in New Issue
Block a user