Introduction
The Sunny Payments API is organized around REST. Our API accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.
Key Concepts
- RESTful Design - Predictable, resource-oriented URLs with standard HTTP methods
- JSON Responses - All responses are returned in JSON format
- Authentication - Secure API key authentication for all requests
- Test Mode - Sandbox environment to test your integration
Authentication
The Sunny API uses API keys to authenticate requests. You can view and manage your API keys in your Dashboard.
API Key Types
sk_test_...Use in sandbox for testing. No real transactions.
sk_live_...Use in production for real transactions.
Making Authenticated Requests
Include your API key in the Authorization header:
curl /api/v1/payments \ -H "Authorization: Bearer sk_test_your_api_key" \ -H "Content-Type: application/json"
Keep your keys secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, or public repositories.
Errors
Sunny uses conventional HTTP response codes to indicate the success or failure of an API request.
HTTP Status Codes
200SuccessRequest succeeded201CreatedResource created successfully400Bad RequestInvalid parameters provided401UnauthorizedInvalid or missing API key404Not FoundResource does not exist429Too Many RequestsRate limit exceeded500Server ErrorSomething went wrong on our endError Response Format
{
"error": "Bad Request",
"message": "Amount, currency, and source are required",
"timestamp": "2024-12-09T10:30:00Z"
}Payments
The Payments API allows you to create, retrieve, and manage payments. Payments represent money moving from a customer to your account.
Endpoints
/paymentsCreate a new payment
/paymentsList all payments
/payments/:idRetrieve a payment
/payments/:id/captureCapture an authorized payment
/payments/:id/refundRefund a payment
Create a Payment
Creates a new payment with the specified amount and source.
curl -X POST /api/v1/payments \
-H "Authorization: Bearer sk_test_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "KES",
"source": {
"type": "card",
"token": "tok_visa"
},
"description": "Order #12345"
}'Response
{
"id": "pay_abc123xyz",
"status": "succeeded",
"amount": 5000,
"currency": "KES",
"description": "Order #12345",
"created_at": "2024-12-09T10:30:00Z"
}Refunds
Refund a payment that has been completed. You can refund the full amount or a partial amount.
/payments/:id/refundCreate a refund
Create a Refund
curl -X POST /api/v1/payments/pay_abc123xyz/refund \
-H "Authorization: Bearer sk_test_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 2500,
"reason": "Customer requested refund"
}'Customers
Customer objects allow you to store payment sources and track payments for your users.
/customersCreate a customer
/customers/:idRetrieve a customer
/customersList all customers
Webhook Events
Webhooks allow you to receive real-time notifications when events happen in your account.
Available Events
payment.succeededpayment.failedpayment.refundedsubscription.createdsubscription.cancelledpayout.completedEvent Payload
{
"id": "evt_1234567890",
"type": "payment.succeeded",
"created_at": "2024-12-09T10:30:00Z",
"data": {
"payment": {
"id": "pay_abc123",
"amount": 5000,
"currency": "KES",
"status": "succeeded"
}
}
}Verifying Signatures
Always verify that webhook requests came from Sunny by checking the signature header.
Each webhook request includes a sunny-signature header. Use your webhook secret to verify this signature before processing the event.
// Verify webhook signature
const signature = request.headers['sunny-signature'];
const payload = request.body;
const secret = process.env.WEBHOOK_SECRET;
// Use HMAC SHA256 to verify
const crypto = require('crypto');
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
if (signature === expected) {
// Signature is valid, process the event
console.log('Webhook verified:', payload.type);
} else {
// Invalid signature, reject the request
return res.status(401).send('Invalid signature');
}