Cancel a Payment
Cancel a pending or processing transaction before the customer completes payment.
Cancel a Payment
POST /v1/transactions/:reference/cancel
Cancels a transaction and fires a payment.cancelled webhook. Only transactions in pending or processing status can be cancelled.
Cancellation is irreversible. Once cancelled, the checkout session is invalidated and the customer cannot complete the payment.
Request
npm install @e-pay/nodenpm install @e-pay/nestjspip install epaypip install epaypip install epaypip install epaycomposer require epay-et/php-sdkcomposer require epay-et/php-sdkimplementation("com.epayethiopia:epay-java:0.1.0")implementation("com.epayethiopia:epay-spring-boot-starter:0.1.0")go get github.com/epay-et/go-sdkNothing NestJS-specific here — this is the same Node.js client call.
Nothing Django-specific here — this is the same Python client call.
Nothing Flask-specific here — this is the same Python client call.
Nothing FastAPI-specific here — this is the same Python client call.
Nothing Laravel-specific here — this is the same PHP client call.
Nothing Spring Boot-specific here — this is the same Java client call.
curl https://api.epayethiopia.com/v1/transactions/PAB12CD3420260813/cancel \
-X POST \
-H "Authorization: Bearer sk_live_your_key_here"import { Epay, EpayBadRequestError } from '@e-pay/node';
const epay = new Epay({ apiKey: process.env.EPAY_SECRET_KEY });
try {
await epay.payments.cancel(reference); // resolves on 204
} catch (error) {
// Already completed, failed, or cancelled.
if (error instanceof EpayBadRequestError) return 'not cancellable';
throw error;
}
const res = await fetch('https://api.epayethiopia.com/v1/transactions/PAB12CD3420260813/cancel', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.EPAY_SECRET_KEY}` },
});
// 204 No Content on success — no body
if (res.status !== 204) {
throw new Error(`ePay ${res.status}: ${await res.text()}`);
}from epay import Epay, EpayBadRequestError
epay = Epay() # reads EPAY_SECRET_KEY
try:
epay.payments.cancel(reference) # returns None on 204
except EpayBadRequestError: # Already completed, failed, or cancelled.
return "not cancellable"
import os
import requests
res = requests.post(
"https://api.epayethiopia.com/v1/transactions/PAB12CD3420260813/cancel",
headers={"Authorization": f"Bearer {os.environ['EPAY_SECRET_KEY']}"},
timeout=30,
)
# 204 No Content on success — no body
if res.status_code != 204:
raise RuntimeError(f"ePay {res.status_code}: {res.text}")use Epay\Epay;
use Epay\Exception\EpayBadRequestException;
$epay = new Epay(); // reads EPAY_SECRET_KEY
try {
$epay->payments->cancel($reference); // returns void on 204
} catch (EpayBadRequestException) {
// Already completed, failed, or cancelled.
return 'not cancellable';
}
$ch = curl_init('https://api.epayethiopia.com/v1/transactions/PAB12CD3420260813/cancel');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('EPAY_SECRET_KEY')],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
// 204 No Content on success — no body
if ($status !== 204) {
throw new RuntimeException("ePay {$status}: {$body}");
}err := client.Payments.Cancel(ctx, reference)
switch {
case errors.Is(err, epay.ErrBadRequest):
// Already completed, failed, or cancelled.
return errNotCancellable
case err != nil:
return err
}
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.epayethiopia.com/v1/transactions/PAB12CD3420260813/cancel", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("EPAY_SECRET_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
// 204 No Content on success — no body
if res.StatusCode != http.StatusNoContent {
return fmt.Errorf("ePay %d", res.StatusCode)
}Epay epay = Epay.fromEnvironment(); // reads EPAY_SECRET_KEY
try {
epay.payments().cancel(reference); // returns on 204
} catch (EpayBadRequestException e) {
// Already completed, failed, or cancelled.
return NOT_CANCELLABLE;
}
HttpClient http = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(
URI.create("https://api.epayethiopia.com/v1/transactions/PAB12CD3420260813/cancel"))
.header("Authorization", "Bearer " + System.getenv("EPAY_SECRET_KEY"))
.POST(HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
// 204 No Content on success — no body
if (response.statusCode() != 204) {
throw new IllegalStateException("ePay " + response.statusCode() + ": " + response.body());
}import {
ConflictException,
Controller,
HttpCode,
Param,
Post,
} from '@nestjs/common';
import { EpayBadRequestError, EpayService } from '@e-pay/nestjs';
@Controller('payments')
export class PaymentsController {
constructor(private readonly epay: EpayService) {}
@Post(':reference/cancel')
@HttpCode(204)
async cancel(@Param('reference') reference: string) {
try {
await this.epay.payments.cancel(reference);
} catch (error) {
// Already completed, failed, or cancelled.
if (error instanceof EpayBadRequestError) {
throw new ConflictException('Payment can no longer be cancelled');
}
throw error;
}
}
}
from django.http import HttpResponse, JsonResponse
from django.views.decorators.http import require_POST
from epay import EpayBadRequestError
from .clients import epay # Epay(api_key=settings.EPAY_SECRET_KEY)
@require_POST
def cancel(request, reference):
try:
epay.payments.cancel(reference)
except EpayBadRequestError:
# Already completed, failed, or cancelled.
return JsonResponse({"detail": "Payment can no longer be cancelled"}, status=409)
return HttpResponse(status=204)from flask import abort
from epay import EpayBadRequestError
from app import app, epay # epay = Epay(), one per process
@app.post("/payments/<reference>/cancel")
def cancel(reference):
try:
epay.payments.cancel(reference)
except EpayBadRequestError: # Already completed, failed, or cancelled.
abort(409, "Payment can no longer be cancelled")
return "", 204
from fastapi import HTTPException
from epay import EpayBadRequestError
from app.main import app, epay # epay = AsyncEpay(), closed in lifespan
@app.post("/payments/{reference}/cancel", status_code=204)
async def cancel(reference: str):
try:
await epay.payments.cancel(reference)
except EpayBadRequestError:
# Already completed, failed, or cancelled.
raise HTTPException(409, "Payment can no longer be cancelled")use Epay\Epay;
use Epay\Exception\EpayBadRequestException;
use Illuminate\Http\Response;
final class PaymentController
{
public function \_\_construct(private readonly Epay $epay) {}
public function cancel(string $reference): Response
{
try {
$this->epay->payments->cancel($reference);
} catch (EpayBadRequestException) {
// Already completed, failed, or cancelled.
abort(409, 'Payment can no longer be cancelled');
}
return response()->noContent();
}
}
// routes/web.php
// Route::post('/payments/{reference}/cancel', [PaymentController::class, 'cancel']);
@RestController
@RequestMapping("/payments")
class PaymentController {
private final Epay epay;
PaymentController(Epay epay) {
this.epay = epay;
}
@PostMapping("/{reference}/cancel")
@ResponseStatus(HttpStatus.NO_CONTENT)
void cancel(@PathVariable String reference) {
try {
epay.payments().cancel(reference);
} catch (EpayBadRequestException e) {
// Already completed, failed, or cancelled.
throw new ResponseStatusException(HttpStatus.CONFLICT, "Payment can no longer be cancelled");
}
}
}Path Parameters
Prop
Type
Response
Returns 204 No Content on success. No response body.
Errors
| Status | Description |
|---|---|
400 | Transaction is not in a cancellable status (completed or failed cannot be cancelled). |
401 | Missing or invalid API key. |
404 | No transaction found for the given reference under your account. |
Next: Webhooks — receive payment.cancelled and other events without polling.