Skip to main content

Initiate Collection (Checkout)

The Initiate Collection endpoint allows you to request payment from a customer. Use this when you want to collect money from users.

Endpoint

POST /payments/collections/checkouts/initiate

Authentication

Required: Yes (Bearer Token)

Authorization: Bearer YOUR_ACCESS_TOKEN

Request

Request Body

{
"payfrom": "+254712345678",
"amount": "1000.00",
"ref_no": "ORDER-2024-001",
"account_number": "1001",
"callback_url": "https://yourdomain.com/webhook",
"category": "ecommerce",
"app_code": 12345
}

Field Descriptions

FieldTypeRequiredDescription
payfromstringYesCustomer phone number (E.164 format)
amountstringYesPayment amount in KES (decimal with 2 places)
ref_nostringYesYour unique reference number (max 50 chars)
account_numberstringYesYour account number for tracking
callback_urlstringYesURL for transaction notifications
categorystringNoTransaction category: ecommerce, gaming, adult_content, forex, or other (default)
app_codeintegerYesApp code for multi-app scenarios

Response

Success Response (200 OK)

{
"payfrom": "+254712345678",
"amount": "1000.00",
"ref_no": "ORDER-2024-001",
"account_number": "1001",
"callback_url": "https://yourdomain.com/webhook",
"status": "pending",
"category": "ecommerce",
"created_at": "12/04/2024 14:30:45"
}

Response Fields

FieldTypeDescription
payfromstringCustomer phone number
amountstringPayment amount (KES)
ref_nostringYour reference number
account_numberstringAccount number used
callback_urlstringWebhook notification URL
statusstringTransaction status
categorystringTransaction category
created_atstringTimestamp (DD/MM/YYYY HH:MM:SS)

Status Codes

CodeStatusDescription
200SuccessCollection initiated successfully
400Bad RequestInvalid parameters
401UnauthorizedInvalid or expired token
403ForbiddenMerchant not approved or app inactive
404Not FoundApp not found
500Server ErrorInternal server error

Error Responses

400 Bad Request - Invalid Phone Number

{
"detail": "Invalid phone number"
}

400 Bad Request - Invalid Amount

{
"detail": "Invalid amount"
}

401 Unauthorized

{
"detail": "Invalid or expired token"
}

403 Forbidden - Merchant Not Approved

{
"detail": "Your merchant profile is not approved. Please ensure your profile has been verified before initiating checkouts."
}

403 Forbidden - App Inactive

{
"detail": "App is not active"
}

403 Forbidden - Product Not Enabled

{
"detail": "App does not have collections product enabled"
}

403 Forbidden - IP Not Allowed

{
"detail": "Access denied: IP address not allowed"
}
info

This error occurs when your app has IP restrictions configured and the request is coming from an IP address that is not in the allowed list. To fix this:

  • Add your server's IP address to the allowed IPs list in your app settings
  • Or remove all IP restrictions if you want to allow requests from any IP address

404 Not Found

{
"detail": "App not found"
}

Examples

curl -X POST https://sandbox.api.jpay.africa/api/v1/payments/collections/checkouts/initiate \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{
"payfrom": "+254712345678",
"amount": "1000.00",
"ref_no": "ORDER-2024-001",
"account_number": "1001",
"callback_url": "https://yourdomain.com/webhook",
"category": "ecommerce"
}'

Collection Categories

Use appropriate categories for collection organization and compliance:

CategoryDescription
ecommerceE-commerce and online shopping transactions
gamingGaming and entertainment transactions
adult_contentAdult content related transactions
forexForex and trading transactions
otherOther transaction types (default)
info

Category Usage: Categories help with transaction organization, reporting, and compliance. Choose the category that best matches your business type.

Important Notes

danger

Before initiating collections, ensure:

  1. ✅ Your merchant account is APPROVED (profile_status = approved)
  2. ✅ Your app has collections product enabled
  3. ✅ Your app is ACTIVE
  4. ✅ Phone number is in E.164 format (e.g., +254712345678)
  5. ✅ Amount has 2 decimal places (e.g., 1000.00)
  6. ✅ Your server's IP address is whitelisted in the app's allowed IPs (if IP restrictions are configured)

Best Practices

Phone Number Validation

Always validate and format phone numbers before sending:

import phonenumbers

def format_phone_number(phone):
try:
parsed = phonenumbers.parse(phone, "KE")
if phonenumbers.is_valid_number(parsed):
return phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
except phonenumbers.NumberParseException:
pass
return None

Error Handling

Implement comprehensive error handling:

def create_collection_with_retry(client, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.post(...)

if response.status_code == 200:
return response.json()

elif response.status_code == 401:
# Token expired, refresh and retry
client.refresh_token()
continue

elif response.status_code == 403:
# Merchant not approved, stop
print("Merchant account not approved")
break

elif response.status_code == 429:
# Rate limited, wait and retry
time.sleep(2 ** attempt)
continue

else:
print(f"Unexpected error: {response.status_code}")
break

except requests.RequestException as e:
print(f"Request failed: {e}")
if attempt < max_retries - 1:
time.sleep(2 ** attempt)

Amount Validation

Validate amounts before sending:

from decimal import Decimal

def validate_amount(amount):
try:
decimal_amount = Decimal(str(amount))

# Check range
if decimal_amount < Decimal('1.00') or decimal_amount > Decimal('999999.99'):
return False, "Amount must be between 1.00 and 999999.99"

# Check decimal places
if decimal_amount.as_tuple().exponent < -2:
return False, "Amount must have maximum 2 decimal places"

return True, decimal_amount
except:
return False, "Invalid amount format"

Webhook Notifications

When a collection is completed, JPay will POST to your callback_url:

{
"event": "collection.completed",
"transaction_id": "COLL-2024-001",
"ref_no": "ORDER-2024-001",
"payfrom": "+254712345678",
"amount": "1000.00",
"status": "completed",
"notes": "Payment completed successfully",
"beneficiary_kyc": {
"first_name": "John",
"middle_name": "Kamau",
"last_name": "Mwangi"
},
"charges": {
"transaction_fee": "25.00",
"commission": "10.00"
},
"timestamp": "2024-04-12T14:35:00Z"
}

Webhook Payload Fields

FieldTypeDescription
eventstringEvent type (e.g., collection.completed, collection.failed)
transaction_idstringUnique transaction identifier
ref_nostringYour reference number provided during initiation
payfromstringCustomer phone number in E.164 format
amountstringPayment amount in KES (decimal with 2 places)
statusstringPayment status: completed, failed, pending
notesstringDescription of the payment status. Provides details about transaction outcome or failure reason
beneficiary_kycobject or nullKYC information of the payer. null when status is not completed
beneficiary_kyc.first_namestring or nullPayer's first name (may be empty)
beneficiary_kyc.middle_namestring or nullPayer's middle name (may be empty)
beneficiary_kyc.last_namestring or nullPayer's last name (may be empty)
chargesobjectTransaction charges breakdown
charges.transaction_feestringTransaction fee charged in KES. "0.00" when status is not completed
charges.commissionstringCommission amount in KES. "0.00" when status is not completed
timestampstringISO 8601 timestamp of the event
info

KYC Availability: The beneficiary_kyc object is only populated when the payment status is completed. For pending or failed transactions, this field will be null. Individual name fields within the KYC object may also be empty depending on the information available from the payment provider.

info

Charges: The charges object contains transaction fee and commission details. When the payment status is not completed, both transaction_fee and commission will be "0.00".

Next Steps