summaryrefslogtreecommitdiff
path: root/packages/taler-util/src/transactions-types.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-util/src/transactions-types.ts')
-rw-r--r--packages/taler-util/src/transactions-types.ts795
1 files changed, 795 insertions, 0 deletions
diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts
new file mode 100644
index 000000000..cee3de9fa
--- /dev/null
+++ b/packages/taler-util/src/transactions-types.ts
@@ -0,0 +1,795 @@
+/*
+ This file is part of GNU Taler
+ (C) 2019 Taler Systems S.A.
+
+ GNU Taler 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, or (at your option) any later version.
+
+ GNU Taler 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
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Type and schema definitions for the wallet's transaction list.
+ *
+ * @author Florian Dold
+ * @author Torsten Grote
+ */
+
+/**
+ * Imports.
+ */
+import {
+ Codec,
+ buildCodecForObject,
+ codecForAny,
+ codecForBoolean,
+ codecForConstString,
+ codecForEither,
+ codecForList,
+ codecForString,
+ codecOptional,
+} from "./codec.js";
+import {
+ AmountString,
+ InternationalizedString,
+ MerchantInfo,
+ codecForInternationalizedString,
+ codecForMerchantInfo,
+} from "./taler-types.js";
+import { TalerPreciseTimestamp, TalerProtocolTimestamp } from "./time.js";
+import {
+ RefreshReason,
+ ScopeInfo,
+ TalerErrorDetail,
+ TransactionIdStr,
+ TransactionStateFilter,
+ WithdrawalExchangeAccountDetails,
+ codecForScopeInfo,
+} from "./wallet-types.js";
+
+export interface TransactionsRequest {
+ /**
+ * return only transactions in the given currency
+ *
+ * it will be removed in next release
+ *
+ * @deprecated use scopeInfo
+ */
+ currency?: string;
+
+ /**
+ * return only transactions in the given scopeInfo
+ */
+ scopeInfo?: ScopeInfo;
+
+ /**
+ * if present, results will be limited to transactions related to the given search string
+ */
+ search?: string;
+
+ /**
+ * Sort order of the transaction items.
+ * By default, items are sorted ascending by their
+ * main timestamp.
+ *
+ * ascending: ascending by timestamp, but pending transactions first
+ * descending: ascending by timestamp, but pending transactions first
+ * stable-ascending: ascending by timestamp, with pending transactions amidst other transactions
+ * (stable in the sense of: pending transactions don't jump around)
+ */
+ sort?: "ascending" | "descending" | "stable-ascending";
+
+ /**
+ * If true, include all refreshes in the transactions list.
+ */
+ includeRefreshes?: boolean;
+
+ filterByState?: TransactionStateFilter;
+}
+
+export interface TransactionState {
+ major: TransactionMajorState;
+ minor?: TransactionMinorState;
+}
+
+export enum TransactionMajorState {
+ // No state, only used when reporting transitions into the initial state
+ None = "none",
+ Pending = "pending",
+ Done = "done",
+ Aborting = "aborting",
+ Aborted = "aborted",
+ Suspended = "suspended",
+ Dialog = "dialog",
+ SuspendedAborting = "suspended-aborting",
+ Failed = "failed",
+ Expired = "expired",
+ // Only used for the notification, never in the transaction history
+ Deleted = "deleted",
+}
+
+export enum TransactionMinorState {
+ // Placeholder until D37 is fully implemented
+ Unknown = "unknown",
+ Deposit = "deposit",
+ KycRequired = "kyc",
+ AmlRequired = "aml",
+ MergeKycRequired = "merge-kyc",
+ Track = "track",
+ SubmitPayment = "submit-payment",
+ RebindSession = "rebind-session",
+ Refresh = "refresh",
+ Pickup = "pickup",
+ AutoRefund = "auto-refund",
+ User = "user",
+ Bank = "bank",
+ Exchange = "exchange",
+ ClaimProposal = "claim-proposal",
+ CheckRefund = "check-refund",
+ CreatePurse = "create-purse",
+ DeletePurse = "delete-purse",
+ RefreshExpired = "refresh-expired",
+ Ready = "ready",
+ Merge = "merge",
+ Repurchase = "repurchase",
+ BankRegisterReserve = "bank-register-reserve",
+ BankConfirmTransfer = "bank-confirm-transfer",
+ WithdrawCoins = "withdraw-coins",
+ ExchangeWaitReserve = "exchange-wait-reserve",
+ AbortingBank = "aborting-bank",
+ Aborting = "aborting",
+ Refused = "refused",
+ Withdraw = "withdraw",
+ MerchantOrderProposed = "merchant-order-proposed",
+ Proposed = "proposed",
+ RefundAvailable = "refund-available",
+ AcceptRefund = "accept-refund",
+ PaidByOther = "paid-by-other",
+ CompletedByOtherWallet = "completed-by-other-wallet",
+}
+
+export enum TransactionAction {
+ Delete = "delete",
+ Suspend = "suspend",
+ Resume = "resume",
+ Abort = "abort",
+ Fail = "fail",
+ Retry = "retry",
+}
+
+export interface TransactionsResponse {
+ // a list of past and pending transactions sorted by pending, timestamp and transactionId.
+ // In case two events are both pending and have the same timestamp,
+ // they are sorted by the transactionId
+ // (lexically ascending and locale-independent comparison).
+ transactions: Transaction[];
+}
+
+export interface TransactionCommon {
+ // opaque unique ID for the transaction, used as a starting point for paginating queries
+ // and for invoking actions on the transaction (e.g. deleting/hiding it from the history)
+ transactionId: TransactionIdStr;
+
+ // the type of the transaction; different types might provide additional information
+ type: TransactionType;
+
+ // main timestamp of the transaction
+ timestamp: TalerPreciseTimestamp;
+
+ /**
+ * Transaction state, as per DD37.
+ */
+ txState: TransactionState;
+
+ /**
+ * Possible transitions based on the current state.
+ */
+ txActions: TransactionAction[];
+
+ /**
+ * Raw amount of the transaction (exclusive of fees or other extra costs).
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Amount added or removed from the wallet's balance (including all fees and other costs).
+ */
+ amountEffective: AmountString;
+
+ error?: TalerErrorDetail;
+
+ /**
+ * If the transaction minor state is in KycRequired this field is going to
+ * have the location where the user need to go to complete KYC information.
+ */
+ kycUrl?: string;
+}
+
+export type Transaction =
+ | TransactionWithdrawal
+ | TransactionPayment
+ | TransactionRefund
+ | TransactionRefresh
+ | TransactionDeposit
+ | TransactionPeerPullCredit
+ | TransactionPeerPullDebit
+ | TransactionPeerPushCredit
+ | TransactionPeerPushDebit
+ | TransactionInternalWithdrawal
+ | TransactionRecoup
+ | TransactionDenomLoss;
+
+export enum TransactionType {
+ Withdrawal = "withdrawal",
+ InternalWithdrawal = "internal-withdrawal",
+ Payment = "payment",
+ Refund = "refund",
+ Refresh = "refresh",
+ Deposit = "deposit",
+ PeerPushDebit = "peer-push-debit",
+ PeerPushCredit = "peer-push-credit",
+ PeerPullDebit = "peer-pull-debit",
+ PeerPullCredit = "peer-pull-credit",
+ Recoup = "recoup",
+ DenomLoss = "denom-loss",
+}
+
+export enum WithdrawalType {
+ TalerBankIntegrationApi = "taler-bank-integration-api",
+ ManualTransfer = "manual-transfer",
+}
+
+export type WithdrawalDetails =
+ | WithdrawalDetailsForManualTransfer
+ | WithdrawalDetailsForTalerBankIntegrationApi;
+
+interface WithdrawalDetailsForManualTransfer {
+ type: WithdrawalType.ManualTransfer;
+
+ /**
+ * Payto URIs that the exchange supports.
+ *
+ * Already contains the amount and message.
+ *
+ * @deprecated in favor of exchangeCreditAccounts
+ */
+ exchangePaytoUris: string[];
+
+ exchangeCreditAccountDetails?: WithdrawalExchangeAccountDetails[];
+
+ // Public key of the reserve
+ reservePub: string;
+
+ /**
+ * Is the reserve ready for withdrawal?
+ */
+ reserveIsReady: boolean;
+}
+
+interface WithdrawalDetailsForTalerBankIntegrationApi {
+ type: WithdrawalType.TalerBankIntegrationApi;
+
+ /**
+ * Set to true if the bank has confirmed the withdrawal, false if not.
+ * An unconfirmed withdrawal usually requires user-input and should be highlighted in the UI.
+ * See also bankConfirmationUrl below.
+ */
+ confirmed: boolean;
+
+ /**
+ * If the withdrawal is unconfirmed, this can include a URL for user
+ * initiated confirmation.
+ */
+ bankConfirmationUrl?: string;
+
+ // Public key of the reserve
+ reservePub: string;
+
+ /**
+ * Is the reserve ready for withdrawal?
+ */
+ reserveIsReady: boolean;
+
+ exchangeCreditAccountDetails?: WithdrawalExchangeAccountDetails[];
+}
+
+export enum DenomLossEventType {
+ DenomExpired = "denom-expired",
+ DenomVanished = "denom-vanished",
+ DenomUnoffered = "denom-unoffered",
+}
+
+/**
+ * A transaction to indicate financial loss due to denominations
+ * that became unusable for deposits.
+ */
+export interface TransactionDenomLoss extends TransactionCommon {
+ type: TransactionType.DenomLoss;
+ lossEventType: DenomLossEventType;
+ exchangeBaseUrl: string;
+}
+
+/**
+ * A withdrawal transaction (either bank-integrated or manual).
+ */
+export interface TransactionWithdrawal extends TransactionCommon {
+ type: TransactionType.Withdrawal;
+
+ /**
+ * Exchange of the withdrawal.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * Amount that got subtracted from the reserve balance.
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Amount that actually was (or will be) added to the wallet's balance.
+ */
+ amountEffective: AmountString;
+
+ withdrawalDetails: WithdrawalDetails;
+}
+
+/**
+ * Internal withdrawal operation, only reported on request.
+ *
+ * Some transactions (peer-*-credit) internally do a withdrawal,
+ * but only the peer-*-credit transaction is reported.
+ *
+ * The internal withdrawal transaction allows to access the details of
+ * the underlying withdrawal for testing/debugging.
+ *
+ * It is usually not reported, so that amounts of transactions properly
+ * add up, since the amountEffecive of the withdrawal is already reported
+ * in the peer-*-credit transaction.
+ */
+export interface TransactionInternalWithdrawal extends TransactionCommon {
+ type: TransactionType.InternalWithdrawal;
+
+ /**
+ * Exchange of the withdrawal.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * Amount that got subtracted from the reserve balance.
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Amount that actually was (or will be) added to the wallet's balance.
+ */
+ amountEffective: AmountString;
+
+ withdrawalDetails: WithdrawalDetails;
+}
+
+export interface PeerInfoShort {
+ expiration: TalerProtocolTimestamp | undefined;
+ summary: string | undefined;
+}
+
+/**
+ * Credit because we were paid for a P2P invoice we created.
+ */
+export interface TransactionPeerPullCredit extends TransactionCommon {
+ type: TransactionType.PeerPullCredit;
+
+ info: PeerInfoShort;
+ /**
+ * Exchange used.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * Amount that got subtracted from the reserve balance.
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Amount that actually was (or will be) added to the wallet's balance.
+ */
+ amountEffective: AmountString;
+
+ /**
+ * URI to send to the other party.
+ *
+ * Only available in the right state.
+ */
+ talerUri: string | undefined;
+}
+
+/**
+ * Debit because we paid someone's invoice.
+ */
+export interface TransactionPeerPullDebit extends TransactionCommon {
+ type: TransactionType.PeerPullDebit;
+
+ info: PeerInfoShort;
+ /**
+ * Exchange used.
+ */
+ exchangeBaseUrl: string;
+
+ amountRaw: AmountString;
+
+ amountEffective: AmountString;
+}
+
+/**
+ * We sent money via a P2P payment.
+ */
+export interface TransactionPeerPushDebit extends TransactionCommon {
+ type: TransactionType.PeerPushDebit;
+
+ info: PeerInfoShort;
+ /**
+ * Exchange used.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * Amount that got subtracted from the reserve balance.
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Amount that actually was (or will be) added to the wallet's balance.
+ */
+ amountEffective: AmountString;
+
+ /**
+ * URI to accept the payment.
+ *
+ * Only present if the transaction is in a state where the other party can
+ * accept the payment.
+ */
+ talerUri?: string;
+}
+
+/**
+ * We received money via a P2P payment.
+ */
+export interface TransactionPeerPushCredit extends TransactionCommon {
+ type: TransactionType.PeerPushCredit;
+
+ info: PeerInfoShort;
+ /**
+ * Exchange used.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * Amount that got subtracted from the reserve balance.
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Amount that actually was (or will be) added to the wallet's balance.
+ */
+ amountEffective: AmountString;
+}
+
+/**
+ * The exchange revoked a key and the wallet recoups funds.
+ */
+export interface TransactionRecoup extends TransactionCommon {
+ type: TransactionType.Recoup;
+}
+
+export enum PaymentStatus {
+ /**
+ * Explicitly aborted after timeout / failure
+ */
+ Aborted = "aborted",
+
+ /**
+ * Payment failed, wallet will auto-retry.
+ * User should be given the option to retry now / abort.
+ */
+ Failed = "failed",
+
+ /**
+ * Paid successfully
+ */
+ Paid = "paid",
+
+ /**
+ * User accepted, payment is processing.
+ */
+ Accepted = "accepted",
+}
+
+export interface TransactionPayment extends TransactionCommon {
+ type: TransactionType.Payment;
+
+ /**
+ * Additional information about the payment.
+ */
+ info: OrderShortInfo;
+
+ /**
+ * Wallet-internal end-to-end identifier for the payment.
+ */
+ proposalId: string;
+
+ /**
+ * Amount that must be paid for the contract
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Amount that was paid, including deposit, wire and refresh fees.
+ */
+ amountEffective: AmountString;
+
+ /**
+ * Amount that has been refunded by the merchant
+ */
+ totalRefundRaw: AmountString;
+
+ /**
+ * Amount will be added to the wallet's balance after fees and refreshing
+ */
+ totalRefundEffective: AmountString;
+
+ /**
+ * Amount pending to be picked up
+ */
+ refundPending: AmountString | undefined;
+
+ /**
+ * Reference to applied refunds
+ */
+ refunds: RefundInfoShort[];
+
+ /**
+ * Is the wallet currently checking for a refund?
+ */
+ refundQueryActive: boolean;
+
+ /**
+ * Does this purchase has an pos validation
+ */
+ posConfirmation: string | undefined;
+}
+
+export interface OrderShortInfo {
+ /**
+ * Order ID, uniquely identifies the order within a merchant instance
+ */
+ orderId: string;
+
+ /**
+ * Hash of the contract terms.
+ */
+ contractTermsHash: string;
+
+ /**
+ * More information about the merchant
+ */
+ merchant: MerchantInfo;
+
+ /**
+ * Summary of the order, given by the merchant
+ */
+ summary: string;
+
+ /**
+ * Map from IETF BCP 47 language tags to localized summaries
+ */
+ summary_i18n?: InternationalizedString;
+
+ /**
+ * URL of the fulfillment, given by the merchant
+ */
+ fulfillmentUrl?: string;
+
+ /**
+ * Plain text message that should be shown to the user
+ * when the payment is complete.
+ */
+ fulfillmentMessage?: string;
+
+ /**
+ * Translations of fulfillmentMessage.
+ */
+ fulfillmentMessage_i18n?: InternationalizedString;
+}
+
+export interface RefundInfoShort {
+ transactionId: string;
+ timestamp: TalerProtocolTimestamp;
+ amountEffective: AmountString;
+ amountRaw: AmountString;
+}
+
+/**
+ * Summary information about the payment that we got a refund for.
+ */
+export interface RefundPaymentInfo {
+ summary: string;
+ summary_i18n?: InternationalizedString;
+ /**
+ * More information about the merchant
+ */
+ merchant: MerchantInfo;
+}
+
+export interface TransactionRefund extends TransactionCommon {
+ type: TransactionType.Refund;
+
+ // Amount that has been refunded by the merchant
+ amountRaw: AmountString;
+
+ // Amount will be added to the wallet's balance after fees and refreshing
+ amountEffective: AmountString;
+
+ // ID for the transaction that is refunded
+ refundedTransactionId: string;
+
+ paymentInfo: RefundPaymentInfo | undefined;
+}
+
+/**
+ * A transaction shown for refreshes.
+ * Only shown for (1) refreshes not associated with other transactions
+ * and (2) refreshes in an error state.
+ */
+export interface TransactionRefresh extends TransactionCommon {
+ type: TransactionType.Refresh;
+
+ refreshReason: RefreshReason;
+
+ /**
+ * Transaction ID that caused this refresh.
+ */
+ originatingTransactionId?: string;
+
+ /**
+ * Always zero for refreshes
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Fees, i.e. the effective, negative effect of the refresh
+ * on the balance.
+ *
+ * Only applicable for stand-alone refreshes, and zero for
+ * other refreshes where the transaction itself accounts for the
+ * refresh fee.
+ */
+ amountEffective: AmountString;
+
+ refreshInputAmount: AmountString;
+ refreshOutputAmount: AmountString;
+}
+
+export interface DepositTransactionTrackingState {
+ // Raw wire transfer identifier of the deposit.
+ wireTransferId: string;
+ // When was the wire transfer given to the bank.
+ timestampExecuted: TalerProtocolTimestamp;
+ // Total amount transfer for this wtid (including fees)
+ amountRaw: AmountString;
+ // Wire fee amount for this exchange
+ wireFee: AmountString;
+}
+
+/**
+ * Deposit transaction, which effectively sends
+ * money from this wallet somewhere else.
+ */
+export interface TransactionDeposit extends TransactionCommon {
+ type: TransactionType.Deposit;
+
+ depositGroupId: string;
+
+ /**
+ * Target for the deposit.
+ */
+ targetPaytoUri: string;
+
+ /**
+ * Raw amount that is being deposited
+ */
+ amountRaw: AmountString;
+
+ /**
+ * Effective amount that is being deposited
+ */
+ amountEffective: AmountString;
+
+ wireTransferDeadline: TalerProtocolTimestamp;
+
+ wireTransferProgress: number;
+
+ /**
+ * Did all the deposit requests succeed?
+ */
+ deposited: boolean;
+
+ trackingState: Array<DepositTransactionTrackingState>;
+}
+
+export interface TransactionByIdRequest {
+ transactionId: string;
+}
+
+export const codecForTransactionByIdRequest =
+ (): Codec<TransactionByIdRequest> =>
+ buildCodecForObject<TransactionByIdRequest>()
+ .property("transactionId", codecForString())
+ .build("TransactionByIdRequest");
+
+export interface WithdrawalTransactionByURIRequest {
+ talerWithdrawUri: string;
+}
+
+export const codecForWithdrawalTransactionByURIRequest =
+ (): Codec<WithdrawalTransactionByURIRequest> =>
+ buildCodecForObject<WithdrawalTransactionByURIRequest>()
+ .property("talerWithdrawUri", codecForString())
+ .build("WithdrawalTransactionByURIRequest");
+
+export const codecForTransactionsRequest = (): Codec<TransactionsRequest> =>
+ buildCodecForObject<TransactionsRequest>()
+ .property("currency", codecOptional(codecForString()))
+ .property("scopeInfo", codecOptional(codecForScopeInfo()))
+ .property("search", codecOptional(codecForString()))
+ .property(
+ "sort",
+ codecOptional(
+ codecForEither(
+ codecForConstString("ascending"),
+ codecForConstString("descending"),
+ codecForConstString("stable-ascending"),
+ ),
+ ),
+ )
+ .property("includeRefreshes", codecOptional(codecForBoolean()))
+ .build("TransactionsRequest");
+
+// FIXME: do full validation here!
+export const codecForTransactionsResponse = (): Codec<TransactionsResponse> =>
+ buildCodecForObject<TransactionsResponse>()
+ .property("transactions", codecForList(codecForAny()))
+ .build("TransactionsResponse");
+
+export const codecForOrderShortInfo = (): Codec<OrderShortInfo> =>
+ buildCodecForObject<OrderShortInfo>()
+ .property("contractTermsHash", codecForString())
+ .property("fulfillmentMessage", codecOptional(codecForString()))
+ .property(
+ "fulfillmentMessage_i18n",
+ codecOptional(codecForInternationalizedString()),
+ )
+ .property("fulfillmentUrl", codecOptional(codecForString()))
+ .property("merchant", codecForMerchantInfo())
+ .property("orderId", codecForString())
+ .property("summary", codecForString())
+ .property("summary_i18n", codecOptional(codecForInternationalizedString()))
+ .build("OrderShortInfo");
+
+export interface ListAssociatedRefreshesRequest {
+ transactionId: string;
+}
+
+export const codecForListAssociatedRefreshesRequest =
+ (): Codec<ListAssociatedRefreshesRequest> =>
+ buildCodecForObject<ListAssociatedRefreshesRequest>()
+ .property("transactionId", codecForString())
+ .build("ListAssociatedRefreshesRequest");
+
+export interface ListAssociatedRefreshesResponse {
+ transactionIds: string[];
+}