Skip to main content

Overview

Refunds Admin API provides admin control over the refund lifecycle. Includes viewing pending refund queues, force-processing refunds (bypassing store approval), marking externally processed refunds, viewing refund analytics, and bulk deletion. All functions require admin authorization. Location: convex/admins/refundsAdmin.ts

Get Pending Refunds

Lists return requests awaiting refund processing (status picked_up). Returns enriched data including customer name, store name, order total, and days waiting.
paginationOpts
PaginationOptions
required
Pagination options (numItems, cursor)
const pending = await convex.query(api.admins.refundsAdmin.getPendingRefunds, {
  paginationOpts: { numItems: 20, cursor: null }
});
{
  "page": [
    {
      "_id": "rr123456789",
      "_creationTime": 1700000000000,
      "orderId": "o111",
      "storeId": "s222",
      "customerId": "c333",
      "status": "picked_up",
      "reason": "Item was damaged",
      "refundAmount": 85.50,
      "requestDate": 1700000000000,
      "approvedDate": 1700100000000,
      "pickedUpDate": 1700200000000,
      "customerName": "Ahmed Ali",
      "storeName": "Pizza Palace",
      "orderTotal": 125.50,
      "daysWaiting": 3
    }
  ],
  "continueCursor": "abc123",
  "isDone": true,
  "totalPending": 8
}
The daysWaiting is calculated from the pickup completion date (or creation time if pickup date is missing) to now. The reason is extracted from the first item in the return request. totalPending reflects the total count of all pending refunds, not just the current page.

Force Refund

Admin override to force-process a refund, bypassing the normal store approval workflow. Supports two modes: Stripe automated refund or manual marking.
returnRequestId
Id<'returnRequests'>
required
Return request ID to process
adminNotes
string
required
Admin notes explaining the forced refund
processViaStripe
boolean
required
When true, schedules a Stripe refund via processStripeRefundInternal. When false, marks the refund as completed manually with a generated reference ID.
// Via Stripe
const stripeResult = await convex.mutation(api.admins.refundsAdmin.forceRefund, {
  returnRequestId: "rr123456789",
  adminNotes: "Customer escalated to support, refund approved",
  processViaStripe: true
});

// Manual
const manualResult = await convex.mutation(api.admins.refundsAdmin.forceRefund, {
  returnRequestId: "rr123456789",
  adminNotes: "Refund issued via bank transfer",
  processViaStripe: false
});
{
  "success": true,
  "message": "Stripe refund processing initiated"
}
When processViaStripe is true, the refund is scheduled asynchronously via ctx.scheduler.runAfter and the return request stays in picked_up status until Stripe confirms. When false, the status is immediately set to refunded with a reference ID formatted as manual_{timestamp}_{adminUserId}.

Mark Refund Processed

Marks a return request as refunded for refunds processed externally (bank transfer, cash, store credit, etc.). Creates an admin notification for audit tracking.
returnRequestId
Id<'returnRequests'>
required
Return request ID to mark as processed
refundMethod
string
required
Method used for the refund, e.g. bank_transfer, cash, store_credit
externalRefundId
string
External reference number (e.g., bank transaction ID). If omitted, a generated ID is used.
adminNotes
string
required
Admin notes about the refund processing
await convex.mutation(api.admins.refundsAdmin.markRefundProcessed, {
  returnRequestId: "rr123456789",
  refundMethod: "bank_transfer",
  externalRefundId: "TXN-2026-03-001",
  adminNotes: "Refund sent via bank transfer to customer account"
});
null
Throws an error if the return request is already in refunded status. A notification is created via adminNotifications.createNotification for audit tracking. If no externalRefundId is provided, a reference ID is generated as external_{refundMethod}_{timestamp}.

Get Refund Statistics

Returns refund analytics for the admin dashboard, including totals, amounts, and a breakdown by reason category.
dateRange
object
Optional date range filter with startDate and endDate (timestamps in milliseconds)
// All-time stats
const allTime = await convex.query(api.admins.refundsAdmin.getRefundStatistics, {});

// Filtered by date range
const filtered = await convex.query(api.admins.refundsAdmin.getRefundStatistics, {
  dateRange: {
    startDate: 1700000000000,
    endDate: 1702600000000
  }
});
{
  "totalRefunds": 85,
  "pendingRefunds": 8,
  "completedRefunds": 62,
  "totalRefundAmount": 7440.50,
  "averageRefundAmount": 120.01,
  "refundsByReason": {
    "damaged": 25,
    "wrong_item": 18,
    "not_as_described": 12,
    "changed_mind": 15,
    "other": 15
  }
}
Pending refunds are those with picked_up status. Completed refunds are those with refunded status. The averageRefundAmount is calculated only from completed refunds. Reason categorization is based on keyword matching in the return item reasons: “damaged”, “wrong”, “not as described”, and “changed mind”. Reasons not matching any category fall into other.

Bulk Delete Refunds

Permanently deletes multiple return request records. Processes in batches of 50 using internal mutations.
refundIds
Id<'returnRequests'>[]
required
Array of return request IDs to delete
const result = await convex.action(api.admins.refundsAdmin.bulkDeleteRefunds, {
  refundIds: ["rr111", "rr222", "rr333"]
});
{
  "refundsDeleted": 3
}
This is a Convex action that delegates to internal mutations in batches of 50. Returns zero count if an empty array is passed. Silently skips already-deleted records.