> ## Documentation Index
> Fetch the complete documentation index at: https://twigz.arqqin.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Payout Operations

> Administrative payout processing and management

## Overview

Payout Operations API provides administrative functions for processing store payout requests, managing payout workflows, reviewing payout statistics, and handling wallet balance adjustments.

**Location:** `convex/admins/payoutOperations.ts`

***

## Get Pending Payouts

Retrieves pending payout requests for admin review. Can filter by store or return all pending payouts. Returns enriched data including store details, wallet balances, and bank information.

<ParamField path="storeId" type="Id<'stores'>">
  Filter payouts by a specific store
</ParamField>

<ParamField path="limit" type="number">
  Maximum number of records to return (default: 50)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const pendingPayouts = await convex.query(api.admins.payoutOperations.getPendingPayouts, {
    limit: 50
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "_id": "p123456789",
      "_creationTime": 1700000000000,
      "storeId": "s123456789",
      "storeName": "Pizza Palace",
      "storeOwnerName": "Ahmed",
      "storeOwnerPhone": "+971501234567",
      "requestedAmount": 500.00,
      "processingFee": 5.00,
      "netAmount": 495.00,
      "bankDetails": {
        "IBAN": "AE070331234567890123456",
        "bankName": "Emirates NBD",
        "accountHolderName": "Ahmed Ali"
      },
      "status": "requested",
      "requestDate": 1700000000000,
      "approvedDate": null,
      "requestedBy": "store_owner_123",
      "currentWalletBalance": 1200.00,
      "totalEarnings": 5000.00,
      "totalOrders": 150
    }
  ]
  ```
</ResponseExample>

***

## Get Payout Statistics

Returns payout statistics for the admin dashboard, including pending/processing counts, completed totals for today and this month, average processing time, and top stores by payout volume.

<ParamField path="dateRange" type="object">
  Optional date range filter with `startDate` and `endDate` (Unix timestamps in milliseconds)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const stats = await convex.query(api.admins.payoutOperations.getPayoutStatistics, {});
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "totalPendingRequests": 5,
    "totalPendingAmount": 2500.00,
    "totalProcessingRequests": 2,
    "totalProcessingAmount": 1000.00,
    "totalCompletedToday": 3,
    "totalCompletedTodayAmount": 1500.00,
    "totalCompletedThisMonth": 45,
    "totalCompletedThisMonthAmount": 22500.00,
    "averageProcessingTime": 12.5,
    "topStoresByPayouts": [
      {
        "storeId": "s123456789",
        "storeName": "Pizza Palace",
        "totalPayouts": 8,
        "totalAmount": 4000.00
      }
    ]
  }
  ```
</ResponseExample>

<Note>
  `averageProcessingTime` is measured in hours. `topStoresByPayouts` returns up to 10 stores sorted by total payout amount this month.
</Note>

***

## Approve Payout

Approves a payout request. The payout must be in `requested` status. Updates associated wallet transactions to `completed`.

<ParamField path="payoutId" type="Id<'payouts'>" required>
  Payout request ID to approve
</ParamField>

<ParamField path="adminUserId" type="string" required>
  Admin user ID performing the action
</ParamField>

<ParamField path="adminNotes" type="string">
  Optional notes about the approval decision
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.admins.payoutOperations.approvePayout, {
    adminUserId: "admin_123",
    payoutId: "p123456789",
    adminNotes: "Approved after verification"
  });
  ```
</CodeGroup>

***

## Reject Payout

Rejects a payout request and returns the requested amount to the store's available wallet balance. The payout must be in `requested` status. Sets payout status to `cancelled` and wallet transactions to `cancelled`.

<ParamField path="payoutId" type="Id<'payouts'>" required>
  Payout request ID to reject
</ParamField>

<ParamField path="adminUserId" type="string" required>
  Admin user ID performing the action
</ParamField>

<ParamField path="rejectionReason" type="string" required>
  Reason for rejecting the payout
</ParamField>

<ParamField path="adminNotes" type="string">
  Optional additional admin notes
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.admins.payoutOperations.rejectPayout, {
    adminUserId: "admin_123",
    payoutId: "p123456789",
    rejectionReason: "Bank details do not match trade license",
    adminNotes: "Store owner contacted to update bank info"
  });
  ```
</CodeGroup>

***

## Mark Payout As Processing

Marks an approved payout as processing when the bank transfer has been initiated. The payout must be in `approved` status.

<ParamField path="payoutId" type="Id<'payouts'>" required>
  Payout request ID
</ParamField>

<ParamField path="adminUserId" type="string" required>
  Admin user ID performing the action
</ParamField>

<ParamField path="processingMethod" type="string" required>
  Method used to process the payout: `bank_transfer`, `manual`, or `third_party`
</ParamField>

<ParamField path="transactionReference" type="string">
  Bank or third-party transaction reference number
</ParamField>

<ParamField path="adminNotes" type="string">
  Optional admin notes
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.admins.payoutOperations.markPayoutAsProcessing, {
    adminUserId: "admin_123",
    payoutId: "p123456789",
    processingMethod: "bank_transfer",
    transactionReference: "ENBD-TXN-20240115-001"
  });
  ```
</CodeGroup>

***

## Complete Payout

Marks a processing payout as completed. Updates wallet statistics including total payouts and last payout date. The payout must be in `processing` status.

<ParamField path="payoutId" type="Id<'payouts'>" required>
  Payout request ID
</ParamField>

<ParamField path="adminUserId" type="string" required>
  Admin user ID performing the action
</ParamField>

<ParamField path="transactionReference" type="string">
  Final transaction reference (overrides existing if provided)
</ParamField>

<ParamField path="adminNotes" type="string">
  Optional admin notes
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.admins.payoutOperations.completePayout, {
    adminUserId: "admin_123",
    payoutId: "p123456789",
    transactionReference: "TXN123456789"
  });
  ```
</CodeGroup>

***

## Mark Payout As Failed

Marks an approved or processing payout as failed. Optionally restores the requested amount to the store's available wallet balance.

<ParamField path="payoutId" type="Id<'payouts'>" required>
  Payout request ID
</ParamField>

<ParamField path="adminUserId" type="string" required>
  Admin user ID performing the action
</ParamField>

<ParamField path="failureReason" type="string" required>
  Reason for the failure (e.g., invalid IBAN, bank rejection)
</ParamField>

<ParamField path="adminNotes" type="string">
  Optional admin notes
</ParamField>

<ParamField path="restoreBalance" type="boolean">
  Whether to restore the payout amount back to the store's available balance (default: false)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.admins.payoutOperations.markPayoutAsFailed, {
    adminUserId: "admin_123",
    payoutId: "p123456789",
    failureReason: "Bank rejected transfer - invalid IBAN",
    restoreBalance: true,
    adminNotes: "Store owner notified to update bank details"
  });
  ```
</CodeGroup>

<Note>
  When `restoreBalance` is true, the requested amount is returned to the store's available balance and associated wallet transactions are marked as `failed`. Use this when the bank transfer was rejected and the funds should be made available again.
</Note>

***

## Adjust Wallet Balance

Manually adjusts a store's wallet balance. Supports adding/deducting available balance, adding/deducting pending balance, or moving funds from pending to available. Records a wallet transaction for audit purposes.

<ParamField path="storeId" type="Id<'stores'>" required>
  Store ID owning the wallet
</ParamField>

<ParamField path="walletId" type="Id<'storeWallets'>" required>
  Wallet ID to adjust
</ParamField>

<ParamField path="adjustmentType" type="string" required>
  Type of adjustment:

  * `add_balance` — Add to available balance (recorded as `bonus` transaction)
  * `deduct_balance` — Deduct from available balance (recorded as `penalty` transaction)
  * `add_pending` — Add to pending balance
  * `deduct_pending` — Deduct from pending balance
  * `move_pending_to_available` — Transfer amount from pending to available balance
</ParamField>

<ParamField path="amount" type="number" required>
  Adjustment amount (must be positive)
</ParamField>

<ParamField path="reason" type="string" required>
  Reason for the adjustment (recorded in transaction description)
</ParamField>

<ParamField path="adminUserId" type="string" required>
  Admin user ID performing the adjustment
</ParamField>

<ParamField path="adminNotes" type="string">
  Optional additional notes (stored in transaction metadata)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.admins.payoutOperations.adjustWalletBalance, {
    storeId: "s123456789",
    walletId: "w123456789",
    adjustmentType: "add_balance",
    amount: 100.00,
    reason: "Compensation for platform error on order #1234",
    adminUserId: "admin_123",
    adminNotes: "Approved by operations manager"
  });
  ```
</CodeGroup>

<Note>
  All wallet adjustments are audited via wallet transactions. The `move_pending_to_available` type does not create a transaction record as it is a net-zero operation. Deductions will fail if the wallet has insufficient balance.
</Note>
