Skip to content

Refunds

createRefund returns money from a payment that has already completed. For a pre-authorisation that was never captured, cancel it instead. For a card payment that has not settled yet, a merchant with voids enabled can void it instead, which avoids the discount rate (%) (the transaction fee for the original sale still applies).

Note

Only payments with status COMPLETED can be refunded. A PENDING payment — which every ACH payment is for a while — cannot be refunded until it completes. See Payment status.

Headers

The standard three plus an idempotency key — createRefund will not run without one, for the same reason createPayment will not: a blind retry would refund twice.

{
  "x-aptean-apim": "<your api key>",
  "x-aptean-tenant": "<your tenant id>",
  "x-aptean-tenant-secret": "<your tenant secret>",
  "idempotency-key": "<a unique value you generate>"
}

The mutation

mutation CreateRefund($input: CreateRefundInput!) {
  createRefund(input: $input) {
    code
    message
    error
    refund {
      id
      amount
    }
  }
}

Full refund

Omit amount and the whole payment is refunded:

{
  "input": {
    "paymentId": "<the payment id to refund>",
    "refundReason": "Returned goods"
  }
}

Partial refund

Supply amount in minor units:

{
  "input": {
    "paymentId": "<the payment id to refund>",
    "refundReason": "One item returned",
    "amount": 800
  }
}
Field Type Notes
paymentId String Required.
refundReason String Required. 1–65,535 characters. Stored on the refund and visible in the merchant portal.
amount Int Optional. Minor units. Minimum 100 — you cannot refund less than 1.00. Omit for a full refund.
paymentRequestId ID Optional. Refund against a specific payment request where a payment covered several.
customData JSON Optional key–value pairs.

Warning

amount has a minimum of 100 (1.00 in the payment currency). A partial refund of, say, 50 cents is rejected. If you need to return less than that, it cannot be done through this API.

Warning

Omitting amount refunds everything. If you meant a partial refund and left the field out because it was null in your code, you have just refunded the full payment. Make the distinction explicit in your integration rather than relying on a nullable variable.

Multiple partial refunds

You can refund a payment more than once, as long as the running total never exceeds the original amount. Aptean Pay tracks what has already been refunded and rejects anything that would take it over.

The response

{
  "data": {
    "createRefund": {
      "code": "SUCCESS",
      "message": "Refund created.",
      "error": null,
      "refund": {
        "id": "a2808c74-dae8-403f-ab7f-919fac8ae61b",
        "amount": 12000
      }
    }
  }
}

Store refund.id.

Note

As with payments, SUCCESS means accepted, not settled. A card refund typically reaches the payer's statement in a few business days — that timing belongs to their issuer, not to Aptean.

Common failures

These are the messages Aptean Pay actually returns:

Message Cause
Unable to create refund, payment has not completed. Current payment status is … Only COMPLETED payments can be refunded.
Unable to create refund, full amount has already been refunded. Nothing left to refund.
Unable to create refund, partial refund amount will exceed payment amount. amount plus previous refunds is more than the payment.
Unable to create refund, a partial refund must have the amount specified. A partial refund was implied but amount was missing.
Unable to create refund, payment not found. Wrong paymentId, or the wrong environment.
Unable to create refund, refund already exists for given id. The idempotency key was already used.
Unable to create refund, full amount of specified payment request has already been refunded. That payment request is fully refunded already.
idempotency key is required No idempotency-key header.
Field "refundReason" of required type "String!" was not provided. refundReason is mandatory.