Initialize a Payment
Create a payment session and receive a hosted checkout URL to redirect your customer.
Initialize a Payment
POST /v1/transactions/initialize
Creates a payment session and returns a hosted checkout URL. Redirect your customer to checkoutUrl to complete the payment.
Use sk_test_… for sandbox, sk_live_… for production. The mode is inferred
from the key — no separate config needed.
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/initialize \
-X POST \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-H "x-idempotency-key: order_123" \
-d '{
"amount": "250.00",
"currencyCode": "ETB",
"customerPhone": "+251911234567",
"merchantReference": "order_123",
"returnUrl": "https://yourstore.com/order/123/complete"
}'import { Epay } from '@e-pay/node';
const epay = new Epay({ apiKey: process.env.EPAY_SECRET_KEY });
const session = await epay.payments.initialize(
{
amount: '250.00',
currencyCode: 'ETB',
customerPhone: '+251911234567',
merchantReference: 'order_123',
returnUrl: 'https://yourstore.com/order/123/complete',
},
{ idempotencyKey: 'order_123' },
);
// session.reference, session.checkoutUrl, session.expiresAt
redirect(session.checkoutUrl);
const res = await fetch('https://api.epayethiopia.com/v1/transactions/initialize', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.EPAY_SECRET_KEY}`,
'Content-Type': 'application/json',
'x-idempotency-key': 'order_123',
},
body: JSON.stringify({
amount: '250.00',
currencyCode: 'ETB',
customerPhone: '+251911234567',
merchantReference: 'order_123',
returnUrl: 'https://yourstore.com/order/123/complete',
}),
});
if (!res.ok) throw new Error(`ePay ${res.status}: ${await res.text()}`);
const { reference, checkoutUrl, expiresAt } = await res.json();from epay import Epay
epay = Epay() # reads EPAY_SECRET_KEY
session = epay.payments.initialize(
amount="250.00",
currency_code="ETB",
customer_phone="+251911234567",
merchant_reference="order_123",
return_url="https://yourstore.com/order/123/complete",
idempotency_key="order_123",
)
# session["reference"], session["checkoutUrl"], session["expiresAt"]
redirect(session["checkoutUrl"])
import os
import requests
res = requests.post(
"https://api.epayethiopia.com/v1/transactions/initialize",
headers={
"Authorization": f"Bearer {os.environ['EPAY_SECRET_KEY']}",
"x-idempotency-key": "order_123",
},
json={
"amount": "250.00",
"currencyCode": "ETB",
"customerPhone": "+251911234567",
"merchantReference": "order_123",
"returnUrl": "https://yourstore.com/order/123/complete",
},
timeout=30,
)
res.raise_for_status()
session = res.json()
reference = session["reference"]
checkout_url = session["checkoutUrl"]use Epay\Epay;
$epay = new Epay(); // reads EPAY_SECRET_KEY
$session = $epay->payments->initialize([
'amount' => '250.00',
'currencyCode' => 'ETB',
'customerPhone' => '+251911234567',
'merchantReference' => 'order_123',
'returnUrl' => 'https://yourstore.com/order/123/complete',
], idempotencyKey: 'order_123');
// $session['reference'], $session['checkoutUrl'], $session['expiresAt']
header('Location: ' . $session['checkoutUrl']);
$ch = curl_init('https://api.epayethiopia.com/v1/transactions/initialize');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('EPAY_SECRET_KEY'),
'Content-Type: application/json',
'x-idempotency-key: order_123',
],
CURLOPT_POSTFIELDS => json_encode([
'amount' => '250.00',
'currencyCode' => 'ETB',
'customerPhone' => '+251911234567',
'merchantReference' => 'order_123',
'returnUrl' => 'https://yourstore.com/order/123/complete',
]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException("ePay {$status}: {$body}");
}
$session = json_decode($body, true);client, err := epay.NewClient() // reads EPAY_SECRET_KEY
if err != nil {
return err
}
session, err := client.Payments.Initialize(ctx, epay.InitializeParams{
Amount: "250.00",
CurrencyCode: "ETB",
CustomerPhone: "+251911234567",
MerchantReference: "order_123",
ReturnURL: "https://yourstore.com/order/123/complete",
IdempotencyKey: "order_123",
})
if err != nil {
return err
}
// session.Reference, session.CheckoutURL, session.ExpiresAt
http.Redirect(w, r, session.CheckoutURL, http.StatusSeeOther)
payload, _ := json.Marshal(map[string]string{
"amount": "250.00",
"currencyCode": "ETB",
"customerPhone": "+251911234567",
"merchantReference": "order_123",
"returnUrl": "https://yourstore.com/order/123/complete",
})
req, _ := http.NewRequestWithContext(ctx, http.MethodPost,
"https://api.epayethiopia.com/v1/transactions/initialize", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("EPAY_SECRET_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-idempotency-key", "order_123")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode >= 400 {
return fmt.Errorf("ePay %d", res.StatusCode)
}
var session struct {
Reference string `json:"reference"`
CheckoutURL string `json:"checkoutUrl"`
ExpiresAt string `json:"expiresAt"`
}
if err := json.NewDecoder(res.Body).Decode(&session); err != nil {
return err
}import com.epayethiopia.epay.Epay;
import com.epayethiopia.epay.InitializeParams;
import com.epayethiopia.epay.model.InitializedPayment;
Epay epay = Epay.fromEnvironment(); // reads EPAY_SECRET_KEY; thread-safe, share one
InitializedPayment session = epay.payments().initialize(InitializeParams.builder()
.amount("250.00")
.currencyCode("ETB")
.customerPhone("+251911234567")
.merchantReference("order_123")
.returnUrl("https://yourstore.com/order/123/complete")
.idempotencyKey("order_123")
.build());
// session.reference(), session.checkoutUrl(), session.expiresAt()
response.sendRedirect(session.checkoutUrl());
HttpClient http = HttpClient.newHttpClient();
String body = """
{
"amount": "250.00",
"currencyCode": "ETB",
"customerPhone": "+251911234567",
"merchantReference": "order_123",
"returnUrl": "https://yourstore.com/order/123/complete"
}
""";
HttpRequest request = HttpRequest.newBuilder(
URI.create("https://api.epayethiopia.com/v1/transactions/initialize"))
.header("Authorization", "Bearer " + System.getenv("EPAY_SECRET_KEY"))
.header("Content-Type", "application/json")
.header("x-idempotency-key", "order_123")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new IllegalStateException("ePay " + response.statusCode() + ": " + response.body());
}
// {"reference":"…","checkoutUrl":"…","status":"success","expiresAt":"…"}
String session = response.body();// app.module.ts, once: EpayModule.forRoot({ isGlobal: true }) reads EPAY_SECRET_KEY
import { Controller, Param, Post, Redirect } from '@nestjs/common';
import { EpayService } from '@e-pay/nestjs';
@Controller('orders')
export class CheckoutController {
constructor(
private readonly epay: EpayService,
private readonly orders: OrdersService,
) {}
@Post(':id/pay')
@Redirect()
async pay(@Param('id') id: string) {
const order = await this.orders.find(id);
const session = await this.epay.payments.initialize(
{
amount: order.total,
currencyCode: 'ETB',
customerPhone: order.customerPhone,
merchantReference: order.id,
returnUrl: `https://yourstore.com/orders/${order.id}/complete`,
},
{ idempotencyKey: order.id },
);
return { url: session.checkoutUrl, statusCode: 303 };
}
}
# payments/clients.py — one client per process; it pools connections
from django.conf import settings
from epay import Epay
epay = Epay(api_key=settings.EPAY_SECRET_KEY)
# payments/views.py
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_POST
from .clients import epay
from .models import Order
@require_POST
def pay(request, order_id):
order = get_object_or_404(Order, pk=order_id)
session = epay.payments.initialize(
amount=order.total, # a DecimalField stays exact
currency_code="ETB",
customer_phone=order.customer_phone,
merchant_reference=str(order.pk),
return_url=request.build_absolute_uri(f"/orders/{order.pk}/complete"),
idempotency_key=str(order.pk),
)
return redirect(session["checkoutUrl"])from flask import Flask, redirect, url_for
from epay import Epay
app = Flask(**name**)
epay = Epay() # one client per process; reads EPAY_SECRET_KEY
@app.post("/orders/<order_id>/pay")
def pay(order_id):
order = Order.query.get_or_404(order_id)
session = epay.payments.initialize(
amount=order.total,
currency_code="ETB",
customer_phone=order.customer_phone,
merchant_reference=order_id,
return_url=url_for("order_complete", order_id=order_id, _external=True),
idempotency_key=order_id,
)
return redirect(session["checkoutUrl"], code=303)
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from epay import AsyncEpay
epay = AsyncEpay() # reads EPAY_SECRET_KEY
@asynccontextmanager
async def lifespan(app: FastAPI):
yield
await epay.aclose() # release the connection pool on shutdown
app = FastAPI(lifespan=lifespan)
@app.post("/orders/{order_id}/pay")
async def pay(order_id: str):
order = await orders.get(order_id)
session = await epay.payments.initialize(
amount=order.total,
currency_code="ETB",
customer_phone=order.customer_phone,
merchant_reference=order_id,
return_url=f"https://yourstore.com/orders/{order_id}/complete",
idempotency_key=order_id,
)
return RedirectResponse(session["checkoutUrl"], status_code=303)// The service provider is auto-discovered and reads EPAY_SECRET_KEY from .env.
use App\Models\Order;
use Epay\Epay;
use Illuminate\Http\RedirectResponse;
final class CheckoutController
{
public function \_\_construct(private readonly Epay $epay) {}
public function store(Order $order): RedirectResponse
{
$session = $this->epay->payments->initialize([
'amount' => $order->total,
'currencyCode' => 'ETB',
'customerPhone' => $order->customer_phone,
'merchantReference' => (string) $order->id,
'returnUrl' => route('orders.complete', $order),
], idempotencyKey: (string) $order->id);
return redirect()->away($session['checkoutUrl']);
}
}
// routes/web.php
// Route::post('/orders/{order}/pay', [CheckoutController::class, 'store']);
// application.yml
// epay:
// api-key: ${EPAY_SECRET_KEY}
@RestController
class CheckoutController {
private final Epay epay;
private final Orders orders;
CheckoutController(Epay epay, Orders orders) {
this.epay = epay;
this.orders = orders;
}
@PostMapping("/orders/{id}/pay")
ResponseEntity<Void> pay(@PathVariable String id) {
Order order = orders.find(id);
InitializedPayment session = epay.payments().initialize(InitializeParams.builder()
.amount(order.total())
.currencyCode("ETB")
.customerPhone(order.phone())
.merchantReference(order.id())
.returnUrl("https://yourstore.com/orders/" + order.id() + "/complete")
.idempotencyKey(order.id())
.build());
return ResponseEntity.status(HttpStatus.SEE_OTHER)
.location(URI.create(session.checkoutUrl()))
.build();
}
}Response
{
"reference": "PAB12CD3420260813",
"checkoutUrl": "https://checkout.epayethiopia.com/pay/PAB12CD3420260813",
"status": "success",
"expiresAt": "2026-08-13T12:30:00.000Z"
}Headers
Prop
Type
Body
Prop
Type
Response
Prop
Type
Errors
| Status | Description |
|---|---|
400 | Invalid amount value or unrecognized currencyCode. |
401 | Missing or invalid API key. |
404 | No payment source found for your account — contact support. |
429 | Rate limit exceeded. |
Next: Verify a Payment — confirm completion before fulfilling an order.