Easy Labs
PaymentsGuides

Issue a refund

Reverse a captured Transfer in part or in full.

Goal

Refund a successful charge back to the original payment method, in part or in full. Refunds in Easy Labs are first-class Transfers themselves (with type: "REVERSAL") — they link to the original via parent_transfer, fire their own webhooks, and settle on their own timeline. This guide covers the most common cases: full refund of a recent charge, partial refund (e.g. one returned line item), and detecting that a refund has finished.

Prerequisites

  • Easy Labs API key — see Quickstart.
  • @easylabs/node installed.
  • The transferId of the original successful charge. From an Order, this is order.transfer.id.

Implementation

1. Issue a full refund

import { createClient } from "@easylabs/node";

const easy = await createClient({ apiKey: process.env.EASY_API_KEY! });

const { data: original } = await easy.getTransfer(transferId);
const { data: refund } = await easy.createRefund(transferId, {
  refund_amount: original.amount, // full amount, in the smallest currency unit
});

// refund.id is the new reversal Transfer.
// refund.parent_transfer === transferId

The reversal is created in the PENDING state and progresses to SUCCEEDED once the issuer accepts it. The original Transfer's state moves to REVERSED once fully reversed.

2. Issue a partial refund

const { data: partial } = await easy.createRefund(transferId, {
  refund_amount: 1000, // $10.00 of the original $50.00 charge
  tags: { reason: "returned_one_item", internal_rma_id: "RMA-789" },
});

You can issue multiple partial refunds against the same original Transfer up to the original amount. After the sum of reversals equals the original amount, the original transitions to REVERSED.

3. (Optional) Subscribe to the refund webhook

If you want server-side confirmation rather than polling, subscribe to refund.created and refund.updated:

import { EasyWebhooks } from "@easylabs/node";

app.post("/webhooks/easy", async (req, res) => {
  const event = EasyWebhooks.constructEvent(
    req.rawBody,
    req.header("x-easy-webhook-signature") ?? "",
    process.env.EASY_WEBHOOK_SECRET!,
  );
  if (event.type === "refund.updated") {
    // event.data is the reversal Transfer
  }
  res.status(204).end();
});

4. Verify in the dashboard

Open the original Order in the Easy Labs dashboard — you will see the linked reversal under "Refunds" with its own state and amount.

Tradeoffs

  • Refunds always go back to the original Payment Instrument; you cannot re-route a refund to a different card or to a wallet.
  • Crypto Orders are not refundable through createRefund. For confirmed on-chain payments, open a support ticket and reconcile off-chain.
  • Refunds fire refund.created / refund.updated events, NOT payment.updated events. Wire up the right handler.
  • A failed refund (issuer rejects) sets the reversal's state to FAILED; the original Transfer is unchanged. Surface the failure_message to your ops team.

On this page