PaySuite API Documentation
PaySuite enables secure payment processing through MPesa, Emola, and Credit/Debit Card Payments, with real-time transaction updates and detailed reports.
Payment Methods
Available payment methods for your customers:
-
MPesa
- Mobile money payments
- Instant processing
- Requires valid MPesa number
-
Emola
- Digital wallet payments
- Instant processing
- Requires valid Emola account
-
Credit/Debit Card
- Processing time: 1-2 business days
- Requires valid card details
Webhooks
Get instant notifications for payment events.
Setup
- Add your webhook URL in merchant settings
- Save your webhook secret securely
- Use the secret to validate webhooks
Events
payment.success
Sent when payment succeeds.
{
"event": "payment.success",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100.50,
"reference": "INV2024001",
"transaction": {
"id": "tr_123456",
"method": "mpesa",
"paid_at": "2024-02-10T10:15:00.000000Z"
}
},
"created_at": 1708235285,
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
payment.failed
Sent when payment fails.
{
"event": "payment.failed",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100.50,
"reference": "INV2024001",
"error": "Insufficient funds"
},
"created_at": 1708235285,
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
Security
Verify webhook authenticity:
<?php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'];
$secret = 'your_webhook_secret';
$calculatedSignature = hash_hmac('sha256', $payload, $secret);
if (hash_equals($signature, $calculatedSignature)) {
$data = json_decode($payload, true);
// Process the event
}
Headers
| Header | Description |
|---|---|
| X-Webhook-Signature | Request signature |
| X-Account-Id | Your merchant account ID |
| Content-Type | application/json |
| User-Agent | PaySuite-Webhook/1.0 |
Best Practices
- Always verify signatures
- Process events once (check request_id)
- Respond within 5 seconds
- Implement retry handling
- Log events for debugging
- Test in sandbox before going live
Authenticating requests
Include your API token in all requests:
curl -X POST "https://paysuite.tech/api/v1/payments" \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json"
Get your API token from the merchant dashboard under Settings > API Access.
Error Handling
All API responses use this format:
{
"status": "error",
"message": "Invalid input"
}
Common error codes:
- 400: Invalid request data
- 401: Invalid API token
- 402: Payment failed
- 404: Resource not found
- 422: Validation error
- 429: Too many requests (rate limit exceeded)
Rate limits:
- 100 requests per minute per merchant
- Webhook retries: 5 attempts with exponential backoff
Payment Requests
APIs for managing payment requests
Create Payment Request
requires authentication
Creates a new payment request in the system.
Example request:
curl --request POST \
"https://paysuite.tech/api/v1/payments" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"amount\": \"100.50\",
\"reference\": \"INV2024001\",
\"description\": \"Payment for invoice INV2024001\",
\"return_url\": \"https:\\/\\/example.com\\/success\"
}"
const url = new URL(
"https://paysuite.tech/api/v1/payments"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"amount": "100.50",
"reference": "INV2024001",
"description": "Payment for invoice INV2024001",
"return_url": "https:\/\/example.com\/success"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://paysuite.tech/api/v1/payments';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'amount' => '100.50',
'reference' => 'INV2024001',
'description' => 'Payment for invoice INV2024001',
'return_url' => 'https://example.com/success',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://paysuite.tech/api/v1/payments'
payload = {
"amount": "100.50",
"reference": "INV2024001",
"description": "Payment for invoice INV2024001",
"return_url": "https:\/\/example.com\/success"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201):
{
"status": "success",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100.5,
"reference": "INV2024001",
"status": "pending",
"checkout_url": "https://paysuite.test/checkout/550e8400-e29b-41d4-a716-446655440000"
}
}
Example response (422):
{
"status": "error",
"message": "Invalid input"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Payment Request
requires authentication
Returns the details of a specific payment request.
Example request:
curl --request GET \
--get "https://paysuite.tech/api/v1/payments/01H8X9V8X9Y8Z9A8B8C8D8E8F8" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://paysuite.tech/api/v1/payments/01H8X9V8X9Y8Z9A8B8C8D8E8F8"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://paysuite.tech/api/v1/payments/01H8X9V8X9Y8Z9A8B8C8D8E8F8';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://paysuite.tech/api/v1/payments/01H8X9V8X9Y8Z9A8B8C8D8E8F8'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
{
"status": "success",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100.5,
"reference": "INV2024001",
"status": "paid",
"transaction": {
"id": 1,
"status": "completed",
"transaction_id": "MPESA123456",
"paid_at": "2024-02-10T10:15:00.000000Z"
}
}
}
Example response (404):
{
"status": "error",
"message": "Payment request not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Payout Requests
APIs for managing payout requests. Payout statuses: pending, completed, failed, cancelled.
List Payouts
requires authentication
Returns a paginated list of payouts for the authenticated account.
Example request:
curl --request GET \
--get "https://paysuite.tech/api/v1/payouts?page=1&limit=15" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"page\": 11,
\"limit\": 12
}"
const url = new URL(
"https://paysuite.tech/api/v1/payouts"
);
const params = {
"page": "1",
"limit": "15",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"page": 11,
"limit": 12
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://paysuite.tech/api/v1/payouts';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'page' => '1',
'limit' => '15',
],
'json' => [
'page' => 11,
'limit' => 12,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://paysuite.tech/api/v1/payouts'
payload = {
"page": 11,
"limit": 12
}
params = {
'page': '1',
'limit': '15',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, json=payload, params=params)
response.json()Example response (200):
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100.00,
"reference": "PO123ABC456",
"status": "pending",
"description": "Payout request",
"method": "mpesa",
"beneficiary": {"phone": "841234567", "holder": "John Doe"},
"created_at": "2024-02-28T10:00:00.000000Z"
}
],
"links": {...},
"meta": {...}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Payout
requires authentication
Returns the details of a specific payout.
Example request:
curl --request GET \
--get "https://paysuite.tech/api/v1/payouts/01H8X9V8X9Y8Z9A8B8C8D8E8F8" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://paysuite.tech/api/v1/payouts/01H8X9V8X9Y8Z9A8B8C8D8E8F8"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://paysuite.tech/api/v1/payouts/01H8X9V8X9Y8Z9A8B8C8D8E8F8';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://paysuite.tech/api/v1/payouts/01H8X9V8X9Y8Z9A8B8C8D8E8F8'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100,
"reference": "PO123ABC456",
"status": "pending",
"description": "Payout request",
"method": "mpesa",
"beneficiary": {
"phone": "841234567",
"holder": "John Doe"
},
"created_at": "2024-02-28T10:00:00.000000Z"
}
}
Example response (404):
{
"message": "Payout not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Refunds
APIs for managing refunds. Refund statuses: pending, processing, completed, failed, cancelled.
List Refunds
requires authentication
Returns a paginated list of refunds for the authenticated account.
Example request:
curl --request GET \
--get "https://paysuite.tech/api/v1/refunds?page=1&limit=20" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"page\": 4,
\"limit\": 25
}"
const url = new URL(
"https://paysuite.tech/api/v1/refunds"
);
const params = {
"page": "1",
"limit": "20",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"page": 4,
"limit": 25
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://paysuite.tech/api/v1/refunds';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'page' => '1',
'limit' => '20',
],
'json' => [
'page' => 4,
'limit' => 25,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://paysuite.tech/api/v1/refunds'
payload = {
"page": 4,
"limit": 25
}
params = {
'page': '1',
'limit': '20',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, json=payload, params=params)
response.json()Example response (200):
{
"data": [
{
"id": "01H8X9V8X9Y8Z9A8B8C8D8E8F8",
"payment_id": "01H8X9V8X9Y8Z9A8B8C8D8E8F8",
"amount": 50.00,
"status": "completed",
"reason": "Customer requested refund"
}
],
"links": {...},
"meta": {...}
}
Example response (422):
{
"message": "The limit field must not be greater than 100."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create Refund
requires authentication
Creates a new refund request for a completed payment.
Example request:
curl --request POST \
"https://paysuite.tech/api/v1/refunds" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"payment_id\": \"01H8X9V8X9Y8Z9A8B8C8D8E8F8\",
\"amount\": \"50.00\",
\"reason\": \"Customer requested refund\"
}"
const url = new URL(
"https://paysuite.tech/api/v1/refunds"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"payment_id": "01H8X9V8X9Y8Z9A8B8C8D8E8F8",
"amount": "50.00",
"reason": "Customer requested refund"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://paysuite.tech/api/v1/refunds';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'payment_id' => '01H8X9V8X9Y8Z9A8B8C8D8E8F8',
'amount' => '50.00',
'reason' => 'Customer requested refund',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://paysuite.tech/api/v1/refunds'
payload = {
"payment_id": "01H8X9V8X9Y8Z9A8B8C8D8E8F8",
"amount": "50.00",
"reason": "Customer requested refund"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201):
{
"status": "success",
"data": {
"id": "01H8X9V8X9Y8Z9A8B8C8D8E8F8",
"payment_id": "01H8X9V8X9Y8Z9A8B8C8D8E8F8",
"amount": 50,
"status": "pending",
"reason": "Customer requested refund"
}
}
Example response (404):
{
"status": "error",
"message": "Payment not found."
}
Example response (422):
{
"status": "error",
"message": "Payment cannot be refunded. It must be completed and have available refund amount."
}
Example response (422):
{
"status": "error",
"message": "Refund amount cannot exceed available refund amount of {amount}."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Refund
requires authentication
Returns the details of a specific refund including gateway records.
Example request:
curl --request GET \
--get "https://paysuite.tech/api/v1/refunds/laudantium" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://paysuite.tech/api/v1/refunds/laudantium"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://paysuite.tech/api/v1/refunds/laudantium';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://paysuite.tech/api/v1/refunds/laudantium'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
{
"status": "success",
"data": {
"id": "01H8X9V8X9Y8Z9A8B8C8D8E8F8",
"payment_id": "01H8X9V8X9Y8Z9A8B8C8D8E8F8",
"amount": 50.00,
"status": "completed",
"reason": "Customer requested refund",
"payment": {...}
}
}
Example response (404):
{
"status": "error",
"message": "Refund not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.