# Generate and use Statira e-commerce keys

An e-commerce key (`zec_...`) connects one store to one merchant and one existing merchant API key. It inherits that key's permissions, environment, and IP restrictions. It is different from a public/secret key pair and from a partner onboarding key.

## 1. Create the underlying merchant API key

1. Sign in to the Statira merchant dashboard for the correct environment: `https://merchant.paywithstatira.com` (LIVE) or `https://merchant.dev.paywithstatira.com` (TEST).
2. Open **Settings → Create API key**, enter a name, and select the permissions your integration needs.
3. For wallet transfers, grant `wallet.payout` (recipient lookup and sending) and `wallet.view` (wallet balance and transfer verification). For collections, grant `payment.create` and `payment.verify`.
4. Create the key. Copy the newly revealed secret if using the public/secret-key gateway APIs. The e-commerce connection flow below issues a separate `opaqueKey`.
5. Open the key to manage its permissions and IP whitelist. Whitelist the calling server's outbound IP when IP restrictions are enabled. Do not put secrets in browser code or mobile applications.

## 2. Connect the store using hosted merchant login

The store should offer a **Connect Statira** button linking to the appropriate merchant login, with URL-encoded `site_url` and `redirect_url`:

```text
https://merchant.paywithstatira.com/login?site_url=https%3A%2F%2Fshop.example.com&redirect_url=https%3A%2F%2Fshop.example.com%2Fstatira%2Fcallback
```

Use the development merchant domain for TEST connections. The merchant signs in, completes any OTP challenge, selects an API key, and confirms the connection. The login site's environment determines TEST or LIVE.

`site_url` and `redirect_url` must use the same hostname. Use matching HTTPS origins in production. Bind the connect flow to the initiating store administrator's session and reject unsolicited callbacks.

Statira redirects to your callback with:

```text
https://shop.example.com/statira/callback?zijelapay_connect_code=zcc_...
```

The code is single-use and expires after five minutes. Exchange it from your server, using exactly the site URL, redirect URL, and mode used to create the connection:

```http
POST /api/v1/ecommerce/connect/exchange
Content-Type: application/json

{
  "code": "zcc_...",
  "siteUrl": "https://shop.example.com",
  "redirectUrl": "https://shop.example.com/statira/callback",
  "mode": "LIVE"
}
```

The API response envelope's `data` contains:

```json
{
  "requiresTwoFactor": false,
  "challengeToken": null,
  "opaqueKey": "zec_...",
  "merchantId": "<merchant-uuid>",
  "merchantDisplayName": "Example Store",
  "siteUrl": "https://shop.example.com"
}
```

Store `opaqueKey` securely on the store's server, associated with the merchant and environment. Statira stores its hash; do not expect to retrieve it later. Reconnecting the same merchant and site replaces the previous active connection. Revoking the underlying merchant API key also prevents its connected e-commerce key from authorizing requests.

For a custom merchant-authenticated interface, `POST /api/v1/ecommerce/connect/complete` accepts a merchant Bearer session and `{siteUrl, redirectUrl, mode, apiKeyId}`. It returns `data.redirectUrl` containing the connect code. The hosted flow performs this step for you. Store servers do not need the merchant's password or Bearer session.

## Create a hosted checkout with an e-commerce key

Your store's backend creates the checkout using its `zec_...` key. The underlying merchant API key must have `payment.create` to create a checkout and `payment.verify` to verify the resulting transaction. Keep the key on your server; never include it in browser JavaScript.

### 1. Create the payment intent

```bash
curl --request POST \
  --url 'https://api.paywithstatira.com/api/v1/ecommerce/payments/intents' \
  --header "X-Ecommerce-Key: $STATIRA_ECOMMERCE_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "merchantReference": "ORDER-1001",
    "amountMinor": 250000,
    "currencyCode": "NGN",
    "customerEmail": "customer@example.com",
    "customerName": "Jane Doe",
    "description": "Payment for order ORDER-1001",
    "callbackUrl": "https://shop.example.com/payment/callback"
  }'
```

| Field | Meaning |
| --- | --- |
| `amountMinor` | Required positive integer in the currency's smallest unit. `250000` is ₦2,500.00. |
| `currencyCode` | Required currency code, such as `NGN`. |
| `merchantReference` | Your order reference; save it with the Statira payment intent. |
| `customerEmail`, `customerName` | Customer details used by checkout. |
| `description` | Description of the purchase. |
| `callbackUrl` | Your HTTPS return URL. This is a browser redirect, not a webhook endpoint. |
| `feeBearer` | Optional: `CUSTOMER` or `MERCHANT`. Omit to use configured fee handling. |
| `sessionDurationMinutes` | Optional; defaults to 60 for e-commerce checkout. Values are constrained to 5–1440 minutes. |

For testing, replace the base URL with `https://api.dev.paywithstatira.com` and use a TEST connection key. The key determines the merchant and environment; do not send a merchant ID or a public/secret key pair with this request. Any IP restrictions on the underlying key also apply.

### 2. Save the response and redirect the customer

Read these fields from the successful API response envelope:

| Response field | Usage |
| --- | --- |
| `data.id` | Save the payment intent ID against the order. |
| `data.reference` | Save the `ALT_...` payment intent reference. |
| `data.hostedCheckoutUrl` | Redirect the customer to this exact URL; do not construct it yourself. |
| `data.amountQuotes` | Review method-specific fees and final charge amounts. |
| `data.expiresAt` | Checkout expiry time. |

For example, after your server receives the Statira response, it can send an HTTP 303 redirect with `Location` set to `data.hostedCheckoutUrl`.

### 3. Verify before fulfilling the order

Checkout returns the customer to `callbackUrl` with query parameters including `reference` (the `TXN_...` transaction reference), `status`, and `merchantReference` when supplied. Treat these browser parameters as untrusted: use the reference to verify from your server.

```bash
curl --request GET \
  --url 'https://api.paywithstatira.com/api/v1/ecommerce/payments/verify/TXN_REPLACE_WITH_TRANSACTION_REFERENCE' \
  --header "X-Ecommerce-Key: $STATIRA_ECOMMERCE_KEY"
```

Read `data.transaction.status`, `data.transaction.internalReference`, `data.transaction.merchantReference`, and `data.transaction.capturedAmountMinor`. Fulfil only after a server-side verification or a validated payment webhook confirms `SUCCESS`, and reconcile the order and expected charge, including the applicable fee treatment. Handle repeated notifications idempotently so an order is fulfilled once.

The verification endpoint requires the **`TXN_...` transaction reference**, not the `ALT_...` intent reference, intent UUID, or your order reference. Passing an intent reference returns `Transaction not found`. A created checkout is not yet a successful payment, and the customer's return to your callback alone is not proof of payment. Webhooks allow you to process confirmation even if the customer closes the browser.

See the [payment webhook documentation](https://paywithstatira.com/docs#webhooks) for signature validation and event handling.

## 3. Call wallet and payout APIs

Production base URL: `https://api.paywithstatira.com` (LIVE). Development: `https://api.dev.paywithstatira.com` (TEST).

```bash
curl 'https://api.paywithstatira.com/api/v1/ecommerce/wallet' \
  -H 'X-Ecommerce-Key: <opaqueKey>'
```

Save `data.walletId` with the merchant and environment. It is the same identifier as `data.id`.

| Operation | Method and path | Permission |
| --- | --- | --- |
| Create hosted checkout | POST `/api/v1/ecommerce/payments/intents` | `payment.create` |
| Verify checkout transaction | GET `/api/v1/ecommerce/payments/verify/{reference}` | `payment.verify` |
| Wallet balance | GET `/api/v1/ecommerce/wallet` | `wallet.view` |
| Confirm transfer recipient | GET `/api/v1/ecommerce/wallet/transfers/recipient/{walletId}` | `wallet.payout` |
| Send wallet transfer | POST `/api/v1/ecommerce/wallet/transfers` | `wallet.payout` |
| Verify wallet transfer | GET `/api/v1/ecommerce/wallet/transfers/{reference}` | `wallet.view` |
| Bank directory | GET `/api/v1/ecommerce/payouts/banks` | `bank.verify` |
| Verify bank account | POST `/api/v1/ecommerce/payouts/accounts/verify` | `bank.verify` |
| Create bank payout | POST `/api/v1/ecommerce/payouts` | `wallet.payout` |

Send a free wallet-to-wallet transfer:

```bash
curl -X POST 'https://api.paywithstatira.com/api/v1/ecommerce/wallet/transfers' \
  -H 'X-Ecommerce-Key: <opaqueKey>' \
  -H 'Idempotency-Key: store-transfer-123' \
  -H 'Content-Type: application/json' \
  -d '{"recipientWalletId":"33333333-3333-4333-8333-333333333333","amountMinor":50000,"narration":"Invoice 123"}'
```

`50000` means ₦500.00. Confirm the recipient name before sending. Both merchants must be approved and active, use the same currency, and the sender must have sufficient available funds. A wallet transfer is not a bank payout.

Use a unique Idempotency-Key for each intended transfer. Retrying identical details with the same key returns the existing receipt; changing details returns 409. The idempotency scope is the sender's wallet, shared across gateway and e-commerce credentials. Do not switch to a new key after a timeout.

See [wallet transfer guide](wallet-to-wallet-transfers.md) and [OpenAPI contract](wallet-transfers.openapi.json) for payloads, receipts, and errors. Keys must match the server environment. Missing/invalid/revoked keys are rejected; restricted permissions or IP addresses are rejected without transferring funds.

## Verification-only integrations

Select `bank.verify` when creating or editing the underlying merchant API key. It permits bank directory lookup and bank-account verification for both public/secret gateway keys and connected e-commerce keys. It does not permit payouts or wallet transfers; those still require `wallet.payout`. All existing merchant API keys receive the new verification permission once during rollout, including keys underlying e-commerce connections. This does not reactivate revoked keys or grant payout access. Remove `wallet.payout` if a key should only verify bank accounts. Newly created keys must explicitly include `bank.verify` for verification.
