Skip to content

verify() & refund()

Checks the current status of a transaction. Always call this server-side before fulfilling an order — never trust the return URL alone.

zirzir.verify(txRef: string): Promise<Transaction>
ParameterTypeDescription
txRefstringYour transaction reference passed to charge()
const tx = await zirzir.verify('invoice_2024_001')
switch (tx.status) {
case 'success':
await fulfillOrder(tx.txRef)
break
case 'pending':
// Payment still in progress — check again or wait for webhook
break
case 'failed':
// Payment failed — show error to customer
break
}
tx = client.verify("invoice_2024_001")
if tx.status == "success":
fulfill_order(tx.tx_ref)
elif tx.status == "pending":
pass # wait for webhook
else:
raise PaymentFailedError(tx.provider_message)
tx, err := client.Verify(ctx, "invoice_2024_001")
if err != nil {
log.Fatal(err)
}
if tx.Status == zirzir.StatusSuccess {
fulfillOrder(tx.TxRef)
}

For USSD-based providers (Telebirr, CBEBirr, M-Pesa), charge() returns immediately while the customer completes payment on their phone. You have two options:

Option A: Webhook (Recommended) Configure callbackUrl in charge() and wait for the transaction.success event.

Option B: Poll Call verify() every few seconds until status changes from pending. Keep polling intervals reasonable (5-10 seconds) and set a timeout.

async function pollUntilComplete(txRef: string, maxAttempts = 12): Promise<Transaction> {
for (let i = 0; i < maxAttempts; i++) {
const tx = await zirzir.verify(txRef)
if (tx.status !== 'pending') return tx
await sleep(5000) // 5 second intervals
}
throw new Error('Payment timed out')
}

Issues a full or partial refund for a completed transaction.

Not all providers support programmatic refunds. Check the Provider Guides for each provider’s refund capabilities. Telebirr and CBEBirr refunds, for example, require manual processing through their merchant portal.

zirzir.refund(txRef: string, params?: RefundParams): Promise<Refund>
ParameterTypeRequiredDescription
txRefstringYesTransaction reference to refund
params.amountnumberNoPartial refund amount. Omit for full refund
params.reasonstringNoReason for refund. Passed to provider if supported
interface Refund {
id: string
transactionId: string
txRef: string
amount: number // Amount refunded
currency: string
status: 'pending' | 'success' | 'failed'
provider: ProviderName
reason: string | null
createdAt: string
}
// Full refund
const refund = await zirzir.refund('invoice_2024_001')
// Partial refund (250 ETB of a 500 ETB transaction)
const partialRefund = await zirzir.refund('invoice_2024_001', {
amount: 250,
reason: 'Customer returned half the order'
})
# Full refund
refund = client.refund("invoice_2024_001")
# Partial refund
refund = client.refund(
"invoice_2024_001",
amount=250,
reason="Customer returned half the order"
)
// Full refund
refund, err := client.Refund(ctx, "invoice_2024_001", nil)
// Partial refund
refund, err := client.Refund(ctx, "invoice_2024_001", &zirzir.RefundParams{
Amount: 250,
Reason: "Customer returned half the order",
})
ProviderProgrammatic RefundPartial RefundNotes
ChapaYesYesFull API support
SantimYesNoFull refunds only
TelebirrNoNoManual via merchant portal
CBEBirrNoNoManual via merchant portal
M-PesaYesYesVia M-Pesa Reversal API
AwashNoNoManual
ErrorDescription
ZirzirRefundNotSupportedErrorProvider doesn’t support programmatic refunds
ZirzirRefundAmountExceededErrorRefund amount exceeds original transaction amount
ZirzirTransactionNotRefundableErrorTransaction is not in a refundable state