fdfea02669
When a repeat payment is detected to soon be eligable for sending, but the user has not approve it yet, we show a notification from the background process to entice the users to go and approve it. This popup now also carries a 'disable' text which disables the repeat payment, effectively shutting up this and further notifications. The tricky part to make this work is that the notification is showing while the application is (likely) not actually active. This takes the strategy that the notification carries some extra details. Among others a newly introduced unique id for a notification itself, and also some text to show on actually processing the disable action. The processing just writes a file, to avoid complexity and side-effects. The file will then be read on start up (either foreground of background) to action on the lines in there. So the item will be disabled on first load.
403 lines
14 KiB
C++
403 lines
14 KiB
C++
/*
|
|
* This file is part of the Flowee project
|
|
* Copyright (C) 2020-2024 Tom Zander <tom@flowee.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef PAYMENT_H
|
|
#define PAYMENT_H
|
|
|
|
#include "FloweePay.h"
|
|
#include "RepeatPaymentDetails.h"
|
|
|
|
#include <QSet>
|
|
|
|
#include <primitives/Tx.h>
|
|
#include <memory>
|
|
|
|
class Wallet;
|
|
class PaymentDetail;
|
|
class PaymentDetailOutput;
|
|
class PaymentDetailInputs;
|
|
class PaymentDetailComment;
|
|
class AccountInfo;
|
|
class TxInfoObject;
|
|
|
|
/**
|
|
* This represents a single payment, possibly a complex one.
|
|
*
|
|
* Using the Payment object we allow building a single transaction that can vary in
|
|
* complexity from paying one single address. Using 'PaymentDetails' the payment can
|
|
* be enriched to excert more control over what funds are sent to what remote parties.
|
|
*
|
|
* To enable simple and direct usability of this class there is a small subset of features
|
|
* available directly on the Payment object. Avoiding the need to create or iterate the child
|
|
* details objects.
|
|
*
|
|
* For this we recognize two modes; either there is exactly one payment-destination (output)
|
|
* or there are additional details.
|
|
* In the case of the simple, 1-desination setup we provide the setPaymentAmount() and
|
|
* setPaymentAmountFiat() methods. Represented in the properties paymentAmount and paymentAmountFiat.
|
|
*
|
|
* When the UI allows adding more details, or scanning a payment protocol (which may do the same)
|
|
* you should avoid writing to those two properties and if you do so anyway we may throw an exception
|
|
* which will cause the application to abort.
|
|
*/
|
|
class Payment : public QObject
|
|
{
|
|
Q_OBJECT
|
|
Q_PROPERTY(int feePerByte READ feePerByte WRITE setFeePerByte NOTIFY feePerByteChanged)
|
|
/// The payment-wide amount of funds being sent by this Payment.
|
|
Q_PROPERTY(double paymentAmount READ paymentAmount WRITE setPaymentAmount NOTIFY amountChanged)
|
|
/// The payment-wide amount of funds being sent by this Payment.
|
|
Q_PROPERTY(int paymentAmountFiat READ paymentAmountFiat WRITE setPaymentAmountFiat NOTIFY amountFiatChanged)
|
|
/// The single-output address we will send to
|
|
Q_PROPERTY(QString targetAddress READ targetAddress WRITE setTargetAddress NOTIFY targetAddressChanged)
|
|
// cleaned up and re-formatted
|
|
Q_PROPERTY(QString formattedTargetAddress READ formattedTargetAddress NOTIFY targetAddressChanged)
|
|
// cleaned but short.
|
|
Q_PROPERTY(QString niceAddress READ niceAddress NOTIFY targetAddressChanged)
|
|
Q_PROPERTY(bool preferSchnorr READ preferSchnorr WRITE setPreferSchnorr NOTIFY preferSchnorrChanged)
|
|
/// If input is valid, tx can be prepared. \see prepare()
|
|
Q_PROPERTY(bool isValid READ validate NOTIFY validChanged)
|
|
Q_PROPERTY(QList<QObject*> details READ paymentDetails NOTIFY paymentDetailsChanged)
|
|
Q_PROPERTY(FloweePay::BroadcastStatus broadcastStatus READ broadcastStatus NOTIFY broadcastStatusChanged)
|
|
/// The price of on BCH
|
|
Q_PROPERTY(int fiatPrice READ fiatPrice WRITE setFiatPrice NOTIFY fiatPriceChanged)
|
|
Q_PROPERTY(AccountInfo *account READ currentAccount WRITE setCurrentAccount NOTIFY currentAccountChanged)
|
|
Q_PROPERTY(QString userComment READ userComment WRITE setUserComment NOTIFY userCommentChanged)
|
|
Q_PROPERTY(bool walletNeedsPin READ walletNeedsPin NOTIFY walletPinChanged)
|
|
Q_PROPERTY(bool autoPrepare READ autoPrepare WRITE setAutoPrepare NOTIFY autoPrepareChanged)
|
|
Q_PROPERTY(bool instaPay READ allowInstaPay WRITE setAllowInstaPay NOTIFY allowInstaPayChanged)
|
|
Q_PROPERTY(bool simpleAddressTarget READ simpleAddressTarget NOTIFY simpleAddressTargetChanged FINAL)
|
|
Q_PROPERTY(RepeatPaymentDetails *repeat READ repeatDetails NOTIFY repeatDetailsChanged FINAL)
|
|
|
|
// --- Stuff that becomes available / useful after prepare has been called:
|
|
/// Tx has been prepared
|
|
Q_PROPERTY(bool txPrepared READ txPrepared NOTIFY txPreparedChanged)
|
|
Q_PROPERTY(QString txid READ txid NOTIFY txCreated)
|
|
Q_PROPERTY(int assignedFee READ assignedFee NOTIFY txCreated)
|
|
Q_PROPERTY(int txSize READ txSize NOTIFY txCreated)
|
|
/// If prepare() failed, this is set.
|
|
Q_PROPERTY(QString error READ error NOTIFY errorChanged)
|
|
|
|
Q_PROPERTY(QStringList warnings READ warnings NOTIFY warningsChanged)
|
|
public:
|
|
enum DetailType {
|
|
InputSelector,
|
|
PayToAddress,
|
|
CommentOutput // aka op-return
|
|
};
|
|
Q_ENUM(DetailType)
|
|
|
|
enum Warning {
|
|
InsecureTransport,
|
|
DownloadFailed,
|
|
OfflineWarning
|
|
};
|
|
Q_ENUM(Warning)
|
|
|
|
Payment(QObject *parent = nullptr);
|
|
Payment(const Streaming::ConstBuffer &stored, const std::shared_ptr<Wallet> &assignedWallet);
|
|
|
|
void setFeePerByte(int sats);
|
|
int feePerByte();
|
|
|
|
/**
|
|
* Sats the amount of BCH on our single-detail payment.
|
|
*
|
|
* This method can no longer be used after addExtraOutput() have been called.
|
|
* This method assumes a single output, which is the default for this class.
|
|
*
|
|
* This sets the amount to pay, in Satoshis, to the target address.
|
|
*/
|
|
void setPaymentAmount(double amount);
|
|
/**
|
|
* Returns the total amount of satoshis that are selected by outputs.
|
|
*/
|
|
double paymentAmount() const;
|
|
|
|
void setPaymentAmountFiat(int amount);
|
|
int paymentAmountFiat() const;
|
|
|
|
/**
|
|
* Sets the address to pay to.
|
|
*
|
|
* This method can no longer be used after addExtraOutput has been called.
|
|
* This method assumes a single output, which is the default for this class.
|
|
* @see pasteTargetAddress()
|
|
*/
|
|
void setTargetAddress(const QString &address);
|
|
|
|
/**
|
|
* Returns the address to pay to, as the user typed it.
|
|
*
|
|
* This method can no longer be used after addExtraOutput has been called.
|
|
* This method assumes a single output, which is the default for this class.
|
|
*/
|
|
QString targetAddress() const;
|
|
/**
|
|
* Returns the validated and formatted address to pay to.
|
|
*
|
|
* This method can no longer be used after addExtraOutput has been called.
|
|
* This method assumes a single output, which is the default for this class.
|
|
*/
|
|
QString formattedTargetAddress() const;
|
|
/**
|
|
* The nicest to display address version.
|
|
* This will always return (if available) the BCH style (cash-address) address, without
|
|
* the bitcoincash: prefix.
|
|
*/
|
|
QString niceAddress() const;
|
|
|
|
bool walletNeedsPin() const;
|
|
|
|
/// return true if all fields are correctly populated and we can prepare()
|
|
bool validate() const;
|
|
/// A user error occured during prepare()
|
|
const QString &error() const;
|
|
|
|
Q_INVOKABLE void prepare();
|
|
Q_INVOKABLE void markUserApproved();
|
|
Q_INVOKABLE void reset();
|
|
Q_INVOKABLE PaymentDetail* addExtraOutput();
|
|
Q_INVOKABLE PaymentDetail* addInputSelector();
|
|
Q_INVOKABLE PaymentDetail* addCommentOutput();
|
|
Q_INVOKABLE void remove(PaymentDetail *detail);
|
|
/**
|
|
* Set a should-be-correct address and return true if valid.
|
|
*
|
|
* Calling thie function is very similar to setting the property 'targetAddress'
|
|
* with the main difference that should the address pasted not be a valid one,
|
|
* this method returns false.
|
|
* @see setTargetAddress
|
|
*/
|
|
Q_INVOKABLE bool pasteTargetAddress(const QString &address);
|
|
/**
|
|
* Unlock the account in order to allow funding of this payment.
|
|
*/
|
|
Q_INVOKABLE void decrypt(const QString &password);
|
|
|
|
/// return the txid, should there be a transaction (otherwise empty string)
|
|
QString txid() const;
|
|
|
|
/// The fee decided to be used in 'prepare()'.
|
|
int assignedFee() const;
|
|
/// The size of the transaction we prepare()d.
|
|
int txSize() const;
|
|
/// Return true if prepare() successfully completed.
|
|
bool txPrepared() const;
|
|
|
|
/**
|
|
* This returns a total fiat amount input into the prepared transaction.
|
|
*/
|
|
int effectiveFiatAmount() const;
|
|
/**
|
|
* This returns a total BCH (in sats) amount that went into the prepared transaction.
|
|
*/
|
|
double effectiveBchAmount() const;
|
|
|
|
/// Return the wallet used by the previous prepare()
|
|
/// \sa currentAccount
|
|
std::shared_ptr<Wallet> wallet() const;
|
|
|
|
bool preferSchnorr() const;
|
|
void setPreferSchnorr(bool preferSchnorr);
|
|
|
|
QList<QObject *> paymentDetails() const;
|
|
|
|
FloweePay::BroadcastStatus broadcastStatus() const;
|
|
|
|
/// returns true if at least one output requires a proper fiat price for payment.
|
|
bool usesFiat() const;
|
|
/// The exchange rate. The amount of cents for one BCH.
|
|
int fiatPrice() const;
|
|
/// The exchange rate. The amount of cents for one BCH.
|
|
void setFiatPrice(int pricePerCoin);
|
|
|
|
AccountInfo *currentAccount() const;
|
|
void setCurrentAccount(AccountInfo *account);
|
|
|
|
const QString &userComment() const;
|
|
void setUserComment(const QString &comment);
|
|
|
|
bool autoPrepare() const;
|
|
void setAutoPrepare(bool newAutoPrepare);
|
|
|
|
bool allowInstaPay() const;
|
|
void setAllowInstaPay(bool allowIt);
|
|
|
|
Tx tx() const;
|
|
|
|
/*
|
|
* When an issue occurred with a consumer of this class and the error should be shown to the user.
|
|
*/
|
|
void forwardError(const QString &error);
|
|
|
|
/**
|
|
* When an warning occurred with a consumer of this class and the warning should be shown to the user.
|
|
*/
|
|
void addWarning(Warning warning);
|
|
QStringList warnings() const;
|
|
Q_INVOKABLE void clearWarnings();
|
|
|
|
/// Bypass the broadcast mechanism and mark the transaction as received.
|
|
void confirmReceivedOk();
|
|
|
|
bool simpleAddressTarget() const;
|
|
void setSimpleAddressTarget(bool newSimpleAddressTarget);
|
|
|
|
Q_INVOKABLE void makeRepeating();
|
|
RepeatPaymentDetails *repeatDetails() const;
|
|
void setRepeatDetails(RepeatPaymentDetails *newRepeatDetails);
|
|
|
|
// Store the Payment and its details in a blob.
|
|
Streaming::ConstBuffer save(const std::shared_ptr<Streaming::BufferPool> &pool) const;
|
|
|
|
/**
|
|
* Deep copy this Payment object and make it repeating, then return an instance of SavedPayment
|
|
*/
|
|
Q_INVOKABLE QObject *copyToRepeating(QObject *parent) const;
|
|
|
|
/**
|
|
* When true, a payment object that results in a transaction will save
|
|
* the payment itself to the wallet that received the transaction.
|
|
* @see Wallet::loadPaymentFor(int index)
|
|
*/
|
|
bool persistPaidPayment() const;
|
|
void setPersistPaidPayment(bool newPersistPaidPayment);
|
|
|
|
int paymentId() const;
|
|
|
|
private slots:
|
|
void recalcAmounts();
|
|
void broadcast();
|
|
|
|
signals:
|
|
void feePerByteChanged();
|
|
void amountChanged();
|
|
void amountFiatChanged();
|
|
void targetAddressChanged();
|
|
void txPreparedChanged();
|
|
void preferSchnorrChanged();
|
|
void paymentDetailsChanged();
|
|
void broadcastStatusChanged();
|
|
void validChanged();
|
|
void errorChanged();
|
|
void txCreated();
|
|
void fiatPriceChanged();
|
|
void currentAccountChanged();
|
|
void userCommentChanged();
|
|
void walletPinChanged();
|
|
void autoPrepareChanged();
|
|
void allowInstaPayChanged();
|
|
void warningsChanged();
|
|
void simpleAddressTargetChanged();
|
|
void approvedByUser();
|
|
void repeatDetailsChanged();
|
|
|
|
private:
|
|
void doAutoPrepare();
|
|
friend class PaymentDetailOutput;
|
|
|
|
/// Helper methods to get the output, assuming that is the only output.
|
|
/// Will throw if the Payment has more than one.
|
|
PaymentDetailOutput *soleOut() const;
|
|
PaymentDetailOutput *firstOut() const;
|
|
void addDetail(PaymentDetail*);
|
|
|
|
AccountInfo *m_account = nullptr;
|
|
|
|
// Payment Variable initialization in reset() please
|
|
QSet<Warning> m_warnings;
|
|
QList<PaymentDetail*> m_paymentDetails;
|
|
bool m_autoPrepare = false;
|
|
bool m_txPrepared;
|
|
bool m_txBroadcastStarted;
|
|
bool m_preferSchnorr;
|
|
bool m_allowInstaPay = false;
|
|
bool m_simpleAddressTarget = true; // only 'advanced' payment protocols set this to false
|
|
bool m_persistPaidPayment = false;
|
|
Tx m_tx;
|
|
int m_fee; // in sats per byte
|
|
int m_assignedFee;
|
|
int m_fiatPrice = 50000; // price for one whole BCH
|
|
int m_paymentId = -1; // how others can refer to this payment without a pointer.
|
|
std::shared_ptr<TxInfoObject> m_infoObject;
|
|
std::shared_ptr<Wallet> m_wallet;
|
|
QString m_error;
|
|
QString m_userComment;
|
|
|
|
RepeatPaymentDetails *m_repeatDetails = nullptr;
|
|
};
|
|
|
|
|
|
class PaymentDetail : public QObject
|
|
{
|
|
Q_OBJECT
|
|
Q_PROPERTY(Payment::DetailType type READ type CONSTANT)
|
|
Q_PROPERTY(bool collapsable READ collapsable WRITE setCollapsable NOTIFY collapsableChanged)
|
|
Q_PROPERTY(bool collapsed READ collapsed WRITE setCollapsed NOTIFY collapsedChanged)
|
|
Q_PROPERTY(bool isOutput READ isOutput CONSTANT)
|
|
Q_PROPERTY(bool isComment READ isComment CONSTANT)
|
|
Q_PROPERTY(bool isInput READ isInputs CONSTANT)
|
|
public:
|
|
PaymentDetail(Payment *parent, Payment::DetailType type);
|
|
|
|
Payment::DetailType type() const;
|
|
|
|
bool collapsable() const;
|
|
void setCollapsable(bool newCollapsable);
|
|
|
|
bool collapsed() const;
|
|
void setCollapsed(bool newCollapsed);
|
|
|
|
inline bool isOutput() const {
|
|
return m_type == Payment::PayToAddress;
|
|
}
|
|
inline bool isInputs() const {
|
|
return m_type == Payment::InputSelector;
|
|
}
|
|
inline bool isComment() const {
|
|
return m_type == Payment::CommentOutput;
|
|
}
|
|
PaymentDetailOutput *toOutput();
|
|
PaymentDetailInputs *toInputs();
|
|
PaymentDetailComment *toComment();
|
|
|
|
bool valid() const;
|
|
|
|
/**
|
|
* When the user selects another wallet (via Payment::setCurrentAccount)
|
|
* default implementation is empty.
|
|
*/
|
|
virtual void setWallet(const std::shared_ptr<Wallet> &wallet);
|
|
|
|
protected:
|
|
void setValid(bool valid);
|
|
|
|
signals:
|
|
void collapsableChanged();
|
|
void collapsedChanged();
|
|
void validChanged();
|
|
|
|
private:
|
|
const Payment::DetailType m_type;
|
|
bool m_collapsable = true;
|
|
bool m_collapsed = false;
|
|
bool m_valid = false; // when all user-input is valid
|
|
};
|
|
|
|
#endif
|