---
title: Merchant Integration Guide
description: Hosted Checkout, Merchant API, request signing, idempotency, and webhook rules.
---

# Merchant Integration Guide

This guide is for merchant developers integrating with Epara. Small merchants can start with no-code Payment Links. Larger merchants should use Merchant API, Hosted Checkout, and Webhooks for automation.

## Integration options

| Option | Who uses it? | When to choose it |
| --- | --- | --- |
| Payment Link | Merchant without engineering team | WhatsApp, Instagram, SMS, or email collection. |
| Hosted Checkout | Merchant with website or app | Epara hosts the secure payment page. |
| Merchant API | Merchant with backend system | Orders, invoices, refunds, payouts, and reports need automation. |
| QR / Code Payment | Physical shop or field collection | Customer pays at counter or in person. |
| Webhooks | Merchant with order system | Merchant system must receive payment, refund, payout, or dispute events. |

## Environments

| Environment | Purpose | Money movement |
| --- | --- | --- |
| test | Integration, demo, QA, and test credential | No real money movement. |
| live | Real merchant and consumer activity | Real provider and ledger records. |

Test and live credentials are separate. A test credential cannot create live payments.

## Base request rule

Merchant API requests must include signing headers:

```http
POST /api/merchant/payment-links HTTP/1.1
Host: api.epara.example
Content-Type: application/json
X-Epara-Key-Id: key_test_...
X-Epara-Timestamp: 2026-06-16T12:00:00Z
X-Epara-Nonce: 0f7d8a1a-6f0b-4c9d-9f92-8d865bb2a111
X-Epara-Signature: hmac_sha256_signature
X-Epara-API-Version: 2026-06-14.basra
Idempotency-Key: plink_school_2026_001
```

## Signing model

Epara signs method, path, timestamp, nonce, API version, and body hash. This proves:

- The request came from the merchant server.
- The body was not modified in transit.
- The nonce was not reused.
- Timestamp drift is acceptable.
- Merchant is using the expected API version.

Canonical string example:

```text
POST
/api/merchant/payment-links
2026-06-16T12:00:00Z
0f7d8a1a-6f0b-4c9d-9f92-8d865bb2a111
2026-06-14.basra
sha256:6fd0a4...
```

## Idempotency

Every request that creates persistent state or moves money must include `Idempotency-Key`.

| Operation | Required? | Why |
| --- | --- | --- |
| Payment create/capture | Yes | Same payment must not be created twice. |
| Refund create | Yes | Same refund must not run twice. |
| Payout request | Yes | Merchant must not be paid twice for one request. |
| Payment Link create | Yes | Retry should return same link. |
| Read/list requests | No | No durable state or money movement. |

## Create Payment Link example

```json
{
  "merchantId": "mrc_demo_market",
  "title": "School fee - June 2026",
  "amount": 255000,
  "currency": "IQD",
  "referencePrefix": "SCH-2026",
  "allowedMethods": ["card", "consumer_wallet", "local_method"],
  "successUrl": "https://merchant.example/success",
  "cancelUrl": "https://merchant.example/cancel"
}
```

Successful response:

```json
{
  "id": "plink_school_2026",
  "status": "active",
  "hostedUrl": "https://pay.epara.example/pay/school-fee",
  "amount": { "amount": 255000, "currency": "IQD" }
}
```

## Webhook events

Merchants must not rely only on redirect URLs. The durable final result should come from signed webhooks.

| Event | When it fires |
| --- | --- |
| `checkout.session.completed` | Hosted Checkout completed successfully. |
| `payment.captured` | Payment became final ledger movement. |
| `payment.failed` | Payment could not complete. |
| `refund.succeeded` | Refund completed successfully. |
| `payout.paid` | Provider confirmed payout paid. |
| `dispute.opened` | Dispute or chargeback case opened. |

## Webhook verification

Merchant should verify:

1. `X-Epara-Webhook-Signature` is valid.
2. Timestamp is inside accepted drift window.
3. Event id was not processed before.
4. Event type is supported.
5. Object id belongs to expected merchant-side object.

## Error model

| HTTP status | Meaning |
| --- | --- |
| 400 | Invalid payload or missing required field. |
| 401 | Missing credential or invalid signature. |
| 403 | Credential scope does not allow operation. |
| 404 | Object not found or does not belong to merchant. |
| 409 | Idempotency or object-state conflict. |
| 422 | Business rule, limit, risk, or verification block. |
| 429 | Rate limit. |
| 500 | Platform internal error. Retry with idempotency. |

## Integration checklist

- Test API credential created.
- Test payment link or checkout session completed.
- Webhook endpoint verifies signed events.
- Retry and idempotency tested.
- Refund flow tested.
- Payout destination and schedule approved.
- Live API credential separated from test credential.
- Live webhook endpoint tested.
- Merchant go-live review completed.

