Core

Payments

Initiate mobile money payments, check status, and handle webhooks. Supports M-Pesa, Airtel Money, Mixx by Yas, and Halotel.

Payment Flow

Here's how a typical payment works end-to-end:

  1. Customer visits payment page (your-site.com/pay/your-slug)
  2. Customer enters phone number and amount
  3. Your backend calls POST /payments/checkout
  4. CamelPay creates a Snippe payment intent
  5. Customer receives USSD prompt on phone
  6. Customer enters PIN to authorize payment
  7. Snippe sends webhook to CamelPay
  8. CamelPay credits your wallet
  9. You receive webhook confirmation

Create a Payment Page

Before accepting payments, create a payment page:

bash
curl -X POST https://api.camelpay.in/payment-pages \
  -H "Authorization: Bearer <your_jwt_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "wallet_topup",
    "title": "Campus Delivery Service",
    "description": "Pay for your campus delivery",
    "amount": 2000,
    "allow_custom_amount": false,
    "currency": "TZS"
  }'

Your payment URL will be: https://camelpay.in/pay/campus-delivery-service

Initiate a Payment

curl -X POST https://api.camelpay.in/payments/checkout \
  -H "X-API-Key: cp_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "campus-delivery-service",
    "phone_number": "+255712345678",
    "customer_name": "John Doe",
    "idempotency_key": "order_12345_67890"
  }'

Request Fields

FieldTypeRequiredDescription
slugstringYesPayment page slug
phone_numberstringYesCustomer's mobile money number (E.164 format)
customer_namestringYesCustomer's name
idempotency_keystringYesUnique identifier for this transaction
amountintegerConditionalRequired if page allows custom amounts

Check Payment Status

bash
curl -X GET https://api.camelpay.in/payments/snippe_ref_abc123/status \
  -H "X-API-Key: cp_live_your_api_key_here"

Status Values

StatusDescription
pendingAwaiting customer action
processingPayment being processed
completedPayment successful, wallet credited
failedPayment failed or declined
voidedCancelled before completion
expiredPayment expired (4 hour timeout)

Polling Strategy

For real-time UI updates, poll the status endpoint:

async function pollPaymentStatus(reference, apiKey) {
  const maxAttempts = 60; // 3 minutes with 3-second intervals
  const interval = 3000;

  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `https://api.camelpay.in/payments/${reference}/status`,
      { headers: { "X-API-Key": apiKey } }
    );
    const { data } = await response.json();

    if (data.status === "completed") return { success: true, data };
    if (data.status === "failed") return { success: false, error: "Payment failed" };

    await new Promise(resolve => setTimeout(resolve, interval));
  }

  return { success: false, error: "Timeout" };
}

Currency and Amounts

  • Currency: TZS (Tanzanian Shilling) only
  • Amount format: Integer (no decimals)
  • Minimum amount: 500 TZS
  • Example: 5000 TZS = 5000 (not 5000.00)