> ## Documentation Index
> Fetch the complete documentation index at: https://docs.niobi.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Collection Best Practices

> Operational guidance, phone number validation rules, reconciliation recipes, and status polling best practices for payment collections.

## Overview

Building a resilient, production-ready payment collection flow requires proper error handling, disciplined polling behavior, strict input validation, and reliable transaction reconciliation. Follow these best practices to ensure seamless transaction processing and avoid common production pitfalls.

***

## 1. When to Query Status vs. Waiting for Callbacks

Niobi processes transactions asynchronously with regional payment switches and mobile network operators.

<div className="pstep-flow">
  <div className="pstep-card pstep-card--initial">
    <div className="pstep-card-header">
      <span className="pstep-badge pstep-badge--initial">Primary Flow</span>
      <span className="pstep-label">Collection Callback</span>
    </div>

    <p className="pstep-desc">Rely on the incoming <code>payment\_step: 2</code> payload.</p>
  </div>

  <span className="pstep-arrow">→</span>

  <div className="pstep-card pstep-card--terminal">
    <div className="pstep-card-header">
      <span className="pstep-badge pstep-badge--terminal">Fallback Flow</span>
      <span className="pstep-label">Transaction Status Query API</span>
    </div>

    <p className="pstep-desc">Query with spaced intervals if the callback isn't received.</p>
  </div>
</div>

### Best Practices for Status Handling:

* **Rely on Callbacks as the Primary Source:**\
  The callback delivered to your `callback_url` is the primary and most efficient notification mechanism.
* **Avoid Aggressive Polling Loops:**\
  Do not poll the status API immediately in a tight loop after initiating a collection. Aggressive polling consumes unnecessary bandwidth and may trigger rate limits.
* **Query Status Using Spaced Intervals:**\
  If your server has not received a callback after a while, you can query the [Get Transaction Status API](/api-reference/get-transaction-status) (`POST /api/v3/get-unified-transaction-status`) using spaced intervals (e.g. progressive backoff) to reconcile pending collection transactions.

***

## 2. The Double-Verification Reconciliation Recipe

For mission-critical workflows where releasing value prematurely or acting on an unverified signal creates financial risk, Niobi recommends implementing a **double-verification reconciliation recipe**: rely on the `payment_step: 2` callback as your primary signal, fall back to a status query if it hasn't arrived, and only close out the order once that confirmed status is reconciled against your own internal order records.

### When to Implement Double-Verification:

* **Instant Digital Value & Wallet Credits:** Crypto on-ramps, forex trading platforms, and digital wallet top-ups where credited funds can be immediately withdrawn.
* **Automated Chain Reactions (Collection to Payout):** Systems where collection completion automatically triggers a downstream disbursement (e.g. cross-border remittances or escrow releases).
* **High-Value B2B Invoicing & Automated Dispatch:** Wholesalers or e-commerce platforms triggering automated warehouse dispatch upon payment confirmation.

### The 4-Step Technical Reconciliation Flow:

```mermaid theme={null}
%%{init: {'themeVariables': {'fontSize': '17px'}}}%%
flowchart TD
    A["Initiate Collection (POST /api/v4/niobi-unified-collections)"] --> B["Receive payment_step: 1 (Initiated)"]
    B --> C["Wait for Incoming Callback"]
    C --> D{"payment_step: 2 Callback Received?"}
    D -- Yes --> E["Reconciliation Step 1: Verify Inbound Signature"]
    D -- "No (after a while)" --> F["Call Status API (POST /api/v3/get-unified-transaction-status)"]
    E --> G["Reconciliation Step 2: Acquire DB Lock on Reference"]
    F --> G
    G --> H{"Reconciliation Step 3: Triple-Point Match?<br/>1. status == 'success' & statusCode == '000000'<br/>2. amount & currency match order<br/>3. third_party_reference_1 matches"}
    H -- "Match Confirmed" --> I["<div style='width:230px'>Reconciliation Step 4: Atomic DB Update (Set status = 'SETTLED') & Credit Balance</div>"]
    H -- "Mismatch / Failed" --> J["<div style='width:230px'>Mark Transaction Failed / Flag for Review</div>"]

    style I fill:#0D9373,stroke:#0D9373,color:#ffffff
    style J fill:#b91c1c,stroke:#b91c1c,color:#ffffff
```

### Reconciliation Checklist:

1. **Inbound Signature Verification:** Always verify the incoming SHA-256 callback signature using your integration Secret Key (`senderKey`) before parsing the payload.
2. **Concurrency Lock & Idempotency Check:** Acquire a row-level database lock on `third_party_reference_1` before processing balance updates. If both a callback and a status query arrive concurrently, only the first thread processes the credit while the second safely exits.
3. **Triple-Point Data Match:** Confirm that all three attributes match your internal order record:
   * **Status & Code:** Both `status: "success"` and `statusCode: "000000"` ([Success Code 000000](/errors/000000-success)).
   * **Amount & Currency:** The received `amount` matches the exact order total (prevents underpayment fulfillment).
   * **Reference:** The `third_party_reference_1` matches the specific customer order ID.
4. **Atomic Ledger Settlement:** Execute the balance credit and update the local order status to `SETTLED` inside a single atomic database transaction.

***

## 3. Phone Number & Amount Formatting Rules

### Strict Integer Rule for `amount` (No Decimal Points)

All collection amounts submitted in API requests **MUST be whole integers** (e.g. `1000` for 1,000 KES/NGN/ZAR). Do not send decimal points (such as `1000.50` or `1000.00`). Submitting decimal amounts will cause the request to fail validation.

### Phone Number Formatting Rules:

* **Digits Only:** Strip all spaces, plus signs (`+`), hyphens, parentheses, or symbols.
* **Length:** Between 8 and 15 digits (including country code).
* **International Prefix without Plus:** Always include the country code, but omit the leading `+` or local trunk `0`.

| Country           | Correct Format  | Incorrect Format                     |
| :---------------- | :-------------- | :----------------------------------- |
| **Kenya**         | `254712345678`  | `+254 712 345 678` or `0712345678`   |
| **Nigeria**       | `2348012345678` | `+234 801 234 5678` or `08012345678` |
| **Ghana**         | `233501234567`  | `+233 50 123 4567` or `0501234567`   |
| **South Africa**  | `27600000008`   | `+27 60 000 0008` or `0600000008`    |
| **Senegal**       | `221770000000`  | `+221 77 000 0000`                   |
| **Côte d'Ivoire** | `225070000000`  | `+225 07 00 00 00`                   |

***

## 4. Error Handling & Retry Policies

Design your integration to distinguish between permanent client errors and temporary upstream issues:

<AccordionGroup>
  <Accordion title="1. Handling 4xx Client Errors (Do Not Auto-Retry)">
    **Examples:** [`400 Bad Request`](/errors/client-errors), [`403 Request not verified`](/errors/authentication-errors), `404 Not Found`.\
    **Action:** Do not retry these requests automatically with the same payload. Log the error message, inspect your parameter validation, verify your signature generation algorithm, and correct the payload before resending. See [400 Client Errors](/errors/client-errors) for code breakdowns.
  </Accordion>

  <Accordion title="2. Handling 5xx Server Errors (Retry with Exponential Backoff)">
    **Examples:** `500 Internal Server Error`, `502 Bad Gateway`, `504 Gateway Timeout`.\
    **Action:** Implement exponential backoff (e.g. retry after 2s, 4s, 8s, 16s) up to a maximum of 3 to 5 attempts. If timeouts persist, check transaction status before re-initiating.
  </Accordion>

  <Accordion title="3. Exact Amount Handling for Virtual Accounts">
    For Nigerian NGN virtual account collections, always collect the exact amount. Any excess payment is automatically reversed by the banking system, which may cause customer confusion if not handled cleanly.
  </Accordion>
</AccordionGroup>

***

## Next Steps

Complete your integration and explore developer references:

<div className="next-steps-flow">
  <a href="/collecting-payments/basics" className="next-step-card">
    <div className="next-step-badge">Review</div>
    <h3>Collection Basics</h3>
    <p>Review the core collection lifecycle, parameters, and payment step transitions.</p>
  </a>

  <div className="next-step-arrow">
    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M5 12h14M12 5l7 7-7 7" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  </div>

  <a href="/transaction-status" className="next-step-card">
    <div className="next-step-badge">Reconciliation</div>
    <h3>Transaction Status & Polling</h3>
    <p>Implementation guide for querying transaction status and reconciliation recipes.</p>
  </a>
</div>
