taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

104-wire-gateway-management-API.rst (7777B)


      1 DD 104: Wire Gateway Management API
      2 ###################################
      3 
      4 :Design status: Draft
      5 :Implementation status: Not started
      6 :DD shepherd: Antoine d'Aligny
      7 :Historical contributors: Antoine d'Aligny
      8 :First published: 2026-09-18
      9 :Last substantive change: 2026-09-18
     10 
     11 Summary
     12 =======
     13 
     14 This design document presents the challenges we currently face when handling
     15 transaction failures in wire gateway adapters and proposes a solution for
     16 addressing them.
     17 
     18 Motivation
     19 ==========
     20 
     21 In the current design and implementations, both incoming and outgoing
     22 transactions can fail without administrators being notified or having the
     23 ability to take corrective action.
     24 
     25 Requirements
     26 ============
     27 
     28 * The solution must allow administrators to see failures, understand their
     29   causes, and possibly fix them.
     30 * The solution must be lightweight to implement in wire gateway adapters, as
     31   the goal of this API is to provide a thin wrapper around how each
     32   implementation works. Common logic that can be extracted into another
     33   component should be.
     34 
     35 Proposed Solution
     36 =================
     37 
     38 Terminology
     39 -----------
     40 
     41 * `in_tx`: final incoming transaction
     42 * `out_tx`: final outgoing transaction
     43 * `talerable_in_tx`: talerable final incoming transaction (RESERVE | KYC | MAP)
     44 * `talerable_out_tx`: talerable final outgoing transaction (deposit)
     45 * `transfer`: initiated talerable outgoing transaction
     46 * `initiated`: initiated outgoing transaction
     47 * `bounce`: bounced invalid outgoing transaction
     48 
     49 Current API State
     50 -----------------
     51 
     52 We already have endpoints for querying Talerable transactions:
     53 `talerable_in_tx` with `GET /history/incoming` and `talerable_out_tx` with
     54 `GET /history/outgoing`.
     55 
     56 We also already have endpoints for querying `transfer` with `GET /transfers`
     57 and `GET /transfers/$ROW_ID`.
     58 
     59 What is missing is access to raw (incompled, malformed, etc) transactions, `in_tx` and `out_tx`, as well
     60 as initiated and bounced transactions, `initiated` and `bounce`.
     61 
     62 Generic API Design
     63 ------------------
     64 
     65 Each adapter works differently. In most adapters, `transfer` and `bounce`
     66 produce an `initiated` operation, while other payment systems, such as
     67 Cyclos, provide a proper way to perform a bounce idempotently and have a
     68 specific bounce operation.
     69 
     70 Each adapter also has different internal unique identifiers. For example,
     71 `libeufin_nexus` uses three of them while `depolymerizer-bitcoin` has a
     72 single transaction ID.
     73 
     74 We need an API that can expose enough detail for manual reconciliation while
     75 remaining generic enough to reuse the same logic across all adapters.
     76 
     77 For `transfer`, we already have the paginated `GET /transfers` endpoint and
     78 the single-entry `GET /transfers/$ROW_ID` endpoint. The problem with this
     79 design is that it is not well suited to following the progress of transfers.
     80 Transfers do not progress or reach finality in the order in which they are
     81 created.
     82 
     83 I propose adding a new status-history endpoint,
     84 `GET /transfers/status-history`. This will allow administrators to understand
     85 the lifecycle of a transaction and track the progress of all transfers in
     86 real time.
     87 
     88 We also need new endpoints for `incoming` and `outgoing` transactions,
     89 including paginated and single-entry endpoints, so that administrators can
     90 inspect all transactions, including incomplete or invalid ones. This is
     91 necessary to understand and debug why an incoming transaction never reaches
     92 the Talerable history.
     93 
     94 Finally, we need a way to track both `initiated` and `bounce` transactions
     95 using new endpoints providing paginated listings, status histories and
     96 single-entry views.
     97 
     98 We could potentially remove all `transfer` endpoints, since transfers are a
     99 subset of `initiated` operatuibs. Whether to do so depends on whether the
    100 transfer-specific API remains useful as a higher-level abstraction.
    101 
    102 We could also enforce a single `initiated` operation concept, event if underneath
    103 bounces works differently. As long as doing this it not too messy for the
    104 database layer we should pursue this simplification.
    105 
    106 .. ts:def:: InTx
    107 
    108   interface InTx {
    109     // Opaque identifier of the returned record.
    110     row_id: SafeUint64;
    111 
    112     // Unstructured implementation specific fields that must be shown.
    113     // Contains at least the unique identifiers to be used for manual reconciliation.
    114     details: {string: string};
    115 
    116     // Date of the transaction.
    117     date: Timestamp;
    118 
    119     // Amount received before credit_fee.
    120     amount?: Amount;
    121 
    122     // Unstructured transaction subject
    123     subject?: string;
    124 
    125     // Fee paid by the creditor.
    126     // If not null, creditor actually received amount - credit_fee
    127     credit_fee?: Amount;
    128 
    129     // Full payto URI to identify the sender of funds.
    130     debit_account?: string;
    131 
    132     // ID of the bounce operation if bounced
    133     bounce_id?: SafeUint64;
    134   }
    135 
    136 .. ts:def:: OutTx
    137 
    138   interface OutTx {
    139     // Opaque identifier of the returned record.
    140     row_id: SafeUint64;
    141 
    142     // Unstructured implementation specific fields that must be shown.
    143     // Contains at least the unique identifiers to be used for manual reconciliation.
    144     details: {string: string};
    145 
    146     // Date of the transaction.
    147     date: Timestamp;
    148 
    149     // Amount received before credit_fee.
    150     amount?: Amount;
    151 
    152     // Unstructured transaction subject
    153     subject?: string;
    154 
    155     // Fee paid by the debtor.
    156     // If not null, debtor actually paid amount + debit_fee
    157     debit_fee?: Amount;
    158 
    159     // Full payto URI to identify the receiver of funds.
    160     credit_account?: string;
    161   }
    162 
    163 .. ts:def:: InitiatedTx
    164 
    165   interface InitiatedTx {
    166     // Opaque ID of this operation.
    167     operation_id: SafeUint64;
    168 
    169     // Amount to transfer.
    170     amount: Amount;
    171 
    172     // The recipient's account identifier as a full payto:// URI.
    173     credit_account: string;
    174 
    175     // Optional ID of the wire transfer initiation if operation is a transfer
    176     transfer_id?: SafeUint64;
    177 
    178     // Optional ID of the bounced incoming transaction if operation is a bounce
    179     bounced_id?: SafeUint64;
    180 
    181     // Current status
    182     // pending: in progress
    183     // transient_failure: has failed but may succeed later
    184     // permanent_failure: has failed permanently and will never reach finality
    185     // success: has succeeded and reached finality
    186     // late_failure: has failed permanently after reaching the success status
    187     status: "pending" | "transient_failure" | "permanent_failure" | "success" | "late_failure";
    188 
    189     // Timestamp that indicates when this status was reached.
    190     timestamp: Timestamp;
    191   }
    192 
    193 .. ts:def:: InitiatedStatus
    194 
    195   interface InitiatedStatus {
    196     // Opaque ID of this operation.
    197     operation_id: SafeUint64;
    198 
    199     // Opaque ID of this status, used for pagination.
    200     row_id: SafeUint64;
    201 
    202     // pending: in progress
    203     // transient_failure: has failed but may succeed later
    204     // permanent_failure: has failed permanently and will never reach finality
    205     // success: has succeeded and reached finality
    206     // late_failure: has failed permanently after reaching the success status
    207     status: "pending" | "transient_failure" | "permanent_failure" | "success" | "late_failure";
    208 
    209     // Optional unstructured messages about the status. Can be used to document the reasons for failure or the state of progress.
    210     status_msg?: string;
    211 
    212     // Timestamp that indicates when this status was reached.
    213     timestamp: Timestamp;
    214   }
    215 
    216 Management logic
    217 ----------------
    218 
    219 On top of this API, we could build a Management SPA to support administrative operations.
    220 The UI would show recent failures and track whether someone has reviewed them and made a decision on how to handle them.
    221 We could also extend the API with administrative operations, such as manually requesting a retry.
    222 
    223 Test Plan
    224 =========
    225 
    226 
    227 
    228 Definition of Done
    229 ==================
    230 
    231 Alternatives
    232 ============
    233 
    234 Drawbacks
    235 =========
    236 
    237 Discussion / Q&A
    238 ================