Skip to main content

Investigating Wallet Rejected payment status

Wallet Rejections are rare and should not be seen too frequently unless the ledger throws out a transaction. This could be the result of something internally wrong with Iroha or simply the wallet having insufficient funds when the transaction was verified.

Iroha's status errors are, unfortunately, very vague and unhelpful so some investigation on your part is required to get to the bottom of the rejection.

  1. Find the payment UUID by searching agency_payments for the effected year = + month = partition for example:

    year = 2022 AND month = 3`

    It helps to sort by status to easily find the wallet rejection in the table.

  2. Select the payment_status row to find the wallet ref string

    payment_id = 0835f1a8-c1db-4d8b-8667-451dea9b6c72
  3. Query the balance using the wallet ref by hitting the GET /wallet/{walletref}/balances endpoint of the wallet service. If the balance is too lowered as if the transaction had succeeded continue investigating. Select the messages events using the wallet ref sting eg:

    persistence_id = 'WalletEntity|b36bqe2fa3tvtcdvhu3f73ahqm4aac@uuid93c907b4-f410-4f6a-8a7b-8f04fbc2b5cc.agency'

    Search the resulting events for the approved payment that failed. look for events with this content:

    Wallet Rejection

    Now follow all events before this point, if you notice multiple approvals happening before this one then one would have to calculate if the balance sufficient enough at the time of failure. If it was, then we could have a duplicate transaction in the ledger that was not recorded by the event system.

    The procedure to add this missing transaction is to find the hash in the ledger and post a specific transfer payload that includes the missing transaction hash in the overrideTransaction field. When the payment UUIDs are confirmed to have not been processed by banking, the funds can be transferred back into the origin wallet.

    1. Query the transactions from the ledger using GET {{ base_url }}/wallet/iroha/{walletref}/transactions

    2. Locate the transaction hash that is not present in the transaction_status table (since it wasn't saved to the DB). Send the following payloads to the POST /wallet/transfer endpoint

    3. Construct a payload that matches the ApprovedPayments event that was rejected eg.

      {
      "fromAccount": "b36bqe2fa3tvtcdvhu3f73ahqm4aac@uuid93c907b4-f410-4f6a-8a7b-8f04fbc2b5cc.agency",
      "transfers": [
      {
      "toAccount": "outgoingeft@phoenix.coin",
      "asset": "zar#phoenix.coin",
      "amount": 1380,
      "description": "Transfer Agency"
      },
      {
      "toAccount": "outgoingeft@phoenix.coin",
      "asset": "zar#phoenix.coin",
      "amount": 8464.44,
      "description": "Transfer Owner"
      },
      {
      "toAccount": "outgoingeft@phoenix.coin",
      "asset": "zar#phoenix.coin",
      "amount": 2155.56,
      "description": "Transfer Body Corporate Levies"
      }
      ],
      "overrideTransaction": "543872f7a241d779b33adf5d6d39e6dd74a66687988c936ca99eb34c5e548e68"
      }

      from account should be the wallet ref you're querying with, to account should match the destination in the approved payments payload. It's important to verify this payload against the transaction too, they must contain identical information for toAccount asset amount and description.

      overrideTransaction Use the transaction hash that is missing from transaction_status table but is present in the ledger (hash that's returned in step 1)

    4. To prevent the accounting service from crashing when it is unable to locate the payment UUID's - assign was_manually_corrected field to true in the accounting payments table - you will have to insert a new row by copying the data from pending_payments for the respective payment UUID.

      Wallet Rejection Pending Payments

    5. For each payment UUID set the payment_kind to the value found in the approved payment object ie. PartyPayment, also set the payment_approved column to true in the payment_status table. If these values are null, the next payload will result in a crash.

    6. Construct a payload to reverse funds back into the origin wallet, it should be similar to the one above but in reverse. Importantly it excludes overrideTransaction and includes the payment UUIDs for each approved payment eg.

      {
      "fromAccount": "outgoingeft@phoenix.coin",
      "transfers": [
      {
      "toAccount": "b36bqe2fa3tvtcdvhu3f73ahqm4aac@uuid93c907b4-f410-4f6a-8a7b-8f04fbc2b5cc.agency",
      "asset": "zar#phoenix.coin",
      "amount": 1380,
      "description": "Transfer Agency",
      "payment": "98fbb580-b53c-407f-9033-074253edde3f"
      },
      {
      "toAccount": "b36bqe2fa3tvtcdvhu3f73ahqm4aac@uuid93c907b4-f410-4f6a-8a7b-8f04fbc2b5cc.agency",
      "asset": "zar#phoenix.coin",
      "amount": 8464.44,
      "description": "Transfer Owner",
      "payment": "0835f1a8-c1db-4d8b-8667-451dea9b6c72"
      },
      {
      "toAccount": "b36bqe2fa3tvtcdvhu3f73ahqm4aac@uuid93c907b4-f410-4f6a-8a7b-8f04fbc2b5cc.agency",
      "asset": "zar#phoenix.coin",
      "amount": 2155.56,
      "description": "Transfer Body Corporate Levies",
      "payment": "4f5906fe-b844-4b77-996a-6d966bc3cdbe"
      }
      ]
      }