Integrate with Cowpay Egyptian payment gateway for accepting payments via Fawry bill payment, credit cards, cash collection, and installments. Use this skill whenever integrating Cowpay, building Egyptian payment solutions, processing EGP transactions, creating Fawry charges, handling card payments, managing cash on delivery, checking payment status, processing refunds, or implementing payment webhooks. Trigger on 'Cowpay', 'Egyptian payment gateway', 'Fawry integration', 'pay at Fawry', 'EGP payments', or 'Cowpay API'.
Cowpay is a flexible payment gateway enabling Egyptian businesses to accept multiple payment methods including Fawry bill payments, credit cards, cash collection (pay on delivery), and installment plans. The API supports full transaction management with webhooks, refunds, and real-time payment status tracking in Egyptian Pounds (EGP).
Use Cowpay when you need to:
Perfect for Egyptian startups, e-commerce platforms, SaaS products, marketplaces, and any business needing flexible Egyptian payment options.
Cowpay uses with merchant credentials and HMAC-SHA256 signing.
Merchant Code: YOUR_MERCHANT_CODE
Merchant Hash Key: YOUR_MERCHANT_HASH_KEY
API Key: YOUR_API_KEY (if applicable)
Store in environment variables:
COWPAY_MERCHANT_CODE=your_merchant_code
COWPAY_MERCHANT_HASH=your_merchant_hash_key
COWPAY_API_KEY=your_api_key
Generate HMAC-SHA256 signature for each request:
const crypto = require('crypto');
function generateCowpaySignature(merchantCode, merchantRefId, customerId, paymentMethod, amount, merchantHash) {
const data = merchantCode + merchantRefId + customerId + paymentMethod + amount + merchantHash;
return crypto.createHash('sha256').update(data).digest('hex');
}
// Example
const signature = generateCowpaySignature(
'MERCHANT123',
'ORD-2025-12345',
'CUST-001',
'PAYATFAWRY',
50000,
'YOUR_MERCHANT_HASH'
);
| Environment | URL |
|---|---|
| Production | https://cowpay.me/api/ |
| Staging | https://staging.cowpay.me/api/ |
Always use staging for testing before production deployment.
Create a Fawry bill payment charge. Customer receives a reference number and pays at any Fawry kiosk nationwide.
POST /fawry/charge-request
Content-Type: application/x-www-form-urlencoded
Request Parameters:
merchant_code=MERCHANT123
merchant_reference_id=ORD-2025-12345
customer_merchant_profile_id=CUST-001
customer_name=Ahmed Hassan
customer_mobile=+201234567890
[email protected]
payment_method=PAYATFAWRY
amount=50000
currency_code=EGP
description=Electronics purchase
charge_items=[{"description":"Laptop","amount":45000,"quantity":1},{"description":"Shipping","amount":5000,"quantity":1}]
signature=<HMAC_SHA256_SIGNATURE>
Response (Success):
{
"type": "CHARGE",
"reference_number": "FAWRY123456789",
"merchant_reference_id": "ORD-2025-12345",
"amount": 50000,
"currency_code": "EGP",
"status_code": 200,
"status_description": "Request processed successfully",
"payment_gateway_reference_id": "PG-2025-xxx",
"expires_at": "2025-02-25T23:59:59Z",
"created_at": "2025-02-23T10:00:00Z"
}
Important:
reference_number is what customer sees and uses at Fawry kiosksProcess direct credit card payment with tokenization for PCI compliance.
POST /card/charge-request
Content-Type: application/x-www-form-urlencoded
Request Parameters:
merchant_code=MERCHANT123
merchant_reference_id=ORD-2025-12346
customer_merchant_profile_id=CUST-002
customer_name=Fatima Hassan
customer_mobile=+201234567890
[email protected]
payment_method=CREDITCARD
amount=50000
currency_code=EGP
card_token=tok_visa_xxxxx
card_cvv=123
card_expiry_month=12
card_expiry_year=26
description=Subscription renewal
signature=<HMAC_SHA256_SIGNATURE>
Response (Success):
{
"type": "CHARGE",
"reference_number": "CARD-123456789",
"merchant_reference_id": "ORD-2025-12346",
"amount": 50000,
"currency_code": "EGP",
"status_code": 200,
"status_description": "Card charged successfully",
"payment_gateway_reference_id": "PG-2025-yyy",
"transaction_reference": "TXN-2025-001",
"card_last_four": "4081",
"card_type": "visa",
"completed_at": "2025-02-23T10:05:00Z",
"created_at": "2025-02-23T10:00:00Z"
}
Security Notes:
Create a cash collection order for pay-on-delivery scenarios.
POST /cashcollection/charge-request
Content-Type: application/x-www-form-urlencoded
Request Parameters:
merchant_code=MERCHANT123
merchant_reference_id=ORD-2025-12347
customer_merchant_profile_id=CUST-003
customer_name=Salma Mohamed
customer_mobile=+201234567890
[email protected]
payment_method=CASHCOLLECTION
amount=50000
currency_code=EGP
delivery_address=123 Main St, Cairo, 11111
delivery_governorate=Cairo
description=Electronics purchase with cash delivery
signature=<HMAC_SHA256_SIGNATURE>
Response (Success):
{
"type": "CHARGE",
"reference_number": "COD-123456789",
"merchant_reference_id": "ORD-2025-12347",
"amount": 50000,
"currency_code": "EGP",
"status_code": 200,
"status_description": "Cash collection order created",
"payment_gateway_reference_id": "PG-2025-zzz",
"collection_reference": "COLLECT-2025-001",
"status": "pending_delivery",
"delivery_address": "123 Main St, Cairo, 11111",
"created_at": "2025-02-23T10:00:00Z"
}
Flow:
payment.completedQuery the status of any charge by merchant reference number.
GET /payment/status
Query Parameters:
merchant_code=MERCHANT123
merchant_reference_id=ORD-2025-12345
signature=<HMAC_SHA256_SIGNATURE>
Response:
{
"type": "PAYMENT_STATUS",
"merchant_reference_id": "ORD-2025-12345",
"reference_number": "FAWRY123456789",
"amount": 50000,
"currency_code": "EGP",
"status_code": 200,
"status_description": "Success",
"payment_status": "completed",
"payment_method": "PAYATFAWRY",
"payment_gateway_reference_id": "PG-2025-xxx",
"paid_at": "2025-02-24T14:30:00Z",
"created_at": "2025-02-23T10:00:00Z"
}
Possible Payment Statuses:
pending - Charge created, awaiting paymentprocessing - Payment in progresscompleted - Payment received and confirmedfailed - Payment failed (declined card, expired Fawry, etc.)cancelled - Transaction cancelled by merchant or customerexpired - Fawry reference expired (24-48 hours)refunded - Refund processedIssue full or partial refund for completed charge.
POST /refund/charge-request
Content-Type: application/x-www-form-urlencoded
Request Parameters:
merchant_code=MERCHANT123
original_merchant_reference_id=ORD-2025-12345
refund_merchant_reference_id=REF-2025-12345
amount=50000
currency_code=EGP
reason=Customer requested refund
refund_items=[{"description":"Product refund","amount":50000,"quantity":1}]
signature=<HMAC_SHA256_SIGNATURE>
Response (Success):
{
"type": "REFUND",
"reference_number": "REFUND-123456789",
"refund_merchant_reference_id": "REF-2025-12345",
"original_merchant_reference_id": "ORD-2025-12345",
"amount": 50000,
"currency_code": "EGP",
"status_code": 200,
"status_description": "Refund processed successfully",
"payment_gateway_reference_id": "PG-2025-ref",
"refund_status": "completed",
"completed_at": "2025-02-23T10:10:00Z",
"created_at": "2025-02-23T10:10:00Z"
}
Notes:
amount to refund full transactionamount for partial refunds (in piasters)Cowpay sends real-time notifications for payment events. Configure callback URL in dashboard.
Always verify webhook signatures to prevent spoofing:
const crypto = require('crypto');
function verifyCowpayWebhook(payload, signature, merchantHash) {
// Reconstruct the data that was signed
const data = payload.merchant_code +
payload.merchant_reference_id +
payload.reference_number +
payload.amount +
merchantHash;
const calculated = crypto.createHash('sha256').update(data).digest('hex');
return calculated === signature;
}
// Express.js example
app.post('/webhooks/cowpay', (req, res) => {
const signature = req.headers['x-request-signature'] || req.body.signature;
if (!verifyCowpayWebhook(req.body, signature, process.env.COWPAY_MERCHANT_HASH)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook
handleCowpayEvent(req.body);
res.json({ success: true });
});
charge.completed
{
"event_type": "charge.completed",
"merchant_code": "MERCHANT123",
"merchant_reference_id": "ORD-2025-12345",
"reference_number": "FAWRY123456789",
"payment_gateway_reference_id": "PG-2025-xxx",
"amount": 50000,
"currency_code": "EGP",
"payment_method": "PAYATFAWRY",
"status": "completed",
"paid_at": "2025-02-24T14:30:00Z",
"signature": "abc123..."
}
charge.failed
{
"event_type": "charge.failed",
"merchant_code": "MERCHANT123",
"merchant_reference_id": "ORD-2025-12346",
"reference_number": "CARD-123456789",
"amount": 50000,
"currency_code": "EGP",
"payment_method": "CREDITCARD",
"status": "failed",
"failure_reason": "Card declined by issuer",
"failure_code": "CARD_DECLINED",
"failed_at": "2025-02-23T10:05:00Z",
"signature": "abc123..."
}
collection.completed
{
"event_type": "collection.completed",
"merchant_code": "MERCHANT123",
"merchant_reference_id": "ORD-2025-12347",
"reference_number": "COD-123456789",
"collection_reference": "COLLECT-2025-001",
"amount": 50000,
"currency_code": "EGP",
"status": "completed",
"collected_at": "2025-02-25T16:45:00Z",
"signature": "abc123..."
}
refund.completed
{
"event_type": "refund.completed",
"merchant_code": "MERCHANT123",
"original_merchant_reference_id": "ORD-2025-12345",
"refund_merchant_reference_id": "REF-2025-12345",
"reference_number": "REFUND-123456789",
"amount": 50000,
"currency_code": "EGP",
"status": "completed",
"refund_completed_at": "2025-02-24T10:00:00Z",
"signature": "abc123..."
}
merchant_reference_id to prevent duplicate processing1. Customer adds items to cart
↓
2. POST /fawry/charge-request
↓
3. Display fawry reference_number to customer
↓
4. Customer pays at any Fawry kiosk within 24-48 hours
↓
5. Receive charge.completed webhook
↓
6. Fulfill order (ship or deliver digital good)
↓
7. Mark order as paid in system
Code Example:
async function createFawryCharge(order) {
const signature = generateCowpaySignature(
process.env.COWPAY_MERCHANT_CODE,
order.id,
order.customerId,
'PAYATFAWRY',
order.amount * 100, // Convert to piasters
process.env.COWPAY_MERCHANT_HASH
);
const response = await fetch('https://cowpay.me/api/fawry/charge-request', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
merchant_code: process.env.COWPAY_MERCHANT_CODE,
merchant_reference_id: order.id,
customer_merchant_profile_id: order.customerId,
customer_name: order.customerName,
customer_mobile: order.customerPhone,
customer_email: order.customerEmail,
payment_method: 'PAYATFAWRY',
amount: order.amount * 100,
currency_code: 'EGP',
description: 'Order payment',
charge_items: JSON.stringify(order.items),
signature: signature
})
});
const data = await response.json();
if (data.status_code === 200) {
// Save reference number
await savePaymentReference(order.id, data.reference_number);
return { success: true, reference: data.reference_number };
}
return { success: false, error: data.status_description };
}
1. Customer enters card details
↓
2. Tokenize card client-side (optional but recommended)
↓
3. POST /card/charge-request with card_token
↓
4. Check response status immediately
↓
5. If 3D Secure required, redirect to bank verification
↓
6. Process result and fulfill order
Code Example:
async function createCardCharge(order, cardToken) {
const signature = generateCowpaySignature(
process.env.COWPAY_MERCHANT_CODE,
order.id,
order.customerId,
'CREDITCARD',
order.amount * 100,
process.env.COWPAY_MERCHANT_HASH
);
const response = await fetch('https://cowpay.me/api/card/charge-request', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
merchant_code: process.env.COWPAY_MERCHANT_CODE,
merchant_reference_id: order.id,
customer_merchant_profile_id: order.customerId,
customer_name: order.customerName,
customer_mobile: order.customerPhone,
customer_email: order.customerEmail,
payment_method: 'CREDITCARD',
amount: order.amount * 100,
currency_code: 'EGP',
card_token: cardToken,
description: 'Product purchase',
signature: signature
})
});
const data = await response.json();
if (data.status_code === 200 && data.status === 'completed') {
await fulfillOrder(order.id);
return { success: true, transactionId: data.transaction_reference };
}
return { success: false, error: data.status_description };
}
1. Customer selects cash on delivery at checkout
↓
2. POST /cashcollection/charge-request
↓
3. Generate shipping label with COD amount
↓
4. Ship product with courier
↓
5. Courier collects cash from customer
↓
6. Receive collection.completed webhook
↓
7. Update order status to fully paid
↓
8. Courier remits payment to merchant
Code Example:
async function createCashCollectionCharge(order) {
const signature = generateCowpaySignature(
process.env.COWPAY_MERCHANT_CODE,
order.id,
order.customerId,
'CASHCOLLECTION',
order.amount * 100,
process.env.COWPAY_MERCHANT_HASH
);
const response = await fetch('https://cowpay.me/api/cashcollection/charge-request', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
merchant_code: process.env.COWPAY_MERCHANT_CODE,
merchant_reference_id: order.id,
customer_merchant_profile_id: order.customerId,
customer_name: order.customerName,
customer_mobile: order.customerPhone,
customer_email: order.customerEmail,
payment_method: 'CASHCOLLECTION',
amount: order.amount * 100,
currency_code: 'EGP',
delivery_address: order.shippingAddress,
delivery_governorate: order.governorate,
description: 'Cash on delivery order',
signature: signature
})
});
const data = await response.json();
if (data.status_code === 200) {
await createShippingLabel(order.id, data.collection_reference);
return { success: true, collectionRef: data.collection_reference };
}
return { success: false, error: data.status_description };
}
Cowpay returns errors with HTTP status codes and descriptive messages.
| Status | Meaning | Action |
|---|---|---|
| 200 | Success | Process response data |
| 400 | Bad Request | Validation error - check parameters |
| 401 | Unauthorized | Invalid credentials or signature |
| 403 | Forbidden | Merchant not authorized for operation |
| 404 | Not Found | Charge/payment not found |
| 422 | Unprocessable Entity | Business logic error (duplicate, expired, etc.) |
| 500 | Server Error | Retry with exponential backoff |
| 503 | Service Unavailable | Temporary outage - retry later |
{
"status_code": 400,
"status_description": "Validation failed",
"error_code": "INVALID_AMOUNT",
"error_message": "Amount must be greater than 100 piasters",
"details": {
"field": "amount",
"value": 50,
"constraint": "minimum"
}
}
| Code | Description | Solution |
|---|---|---|
INVALID_AMOUNT | Amount is invalid (too small, negative, etc.) | Ensure amount ≥ 100 piasters |
INVALID_PHONE | Phone number format incorrect | Use format: +201234567890 |
INVALID_EMAIL | Email format invalid | Use valid email address |
INVALID_SIGNATURE | HMAC signature mismatch | Regenerate signature with correct hash key |
MERCHANT_NOT_FOUND | Merchant code not registered | Verify merchant code is correct |
DUPLICATE_TRANSACTION | Duplicate merchant_reference_id | Use unique reference numbers |
CHARGE_NOT_FOUND | Referenced charge doesn't exist | Verify charge ID/reference number |
CHARGE_EXPIRED | Fawry reference expired | Create new charge request |
REFUND_NOT_ELIGIBLE | Charge cannot be refunded | Only completed charges can be refunded |
INSUFFICIENT_BALANCE | Insufficient funds (cash collection) | Ensure payment collected first |
async function retryWithBackoff(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
// Only retry on server errors (5xx)
if (error.status >= 500) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// Don't retry client errors (4xx)
throw error;
}
}
}
// User enters: 500 EGP
const amountInPiasters = 500 * 100; // 50000
merchant_code + merchant_reference_id + customer_id + payment_method + amount + merchant_hash⚠️ SIGNATURE ORDER — VERIFY WITH COWPAY
Cowpay's signature algorithm concatenates fields in a non-standard order and their public documentation is limited. The order documented above (
merchant_code + merchant_reference_id + customer_id + payment_method + amount + merchant_hash) is based on reported integrations. If signatures fail validation, contact Cowpay's integration team to confirm the exact concatenation order and whether it differs per payment method (Fawry vs card vs COD). Wrong field order = signature mismatch on every request.
ORD-{TIMESTAMP}-{RANDOM} to ensure uniquenessconst merchantRefId = `ORD-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
+20123456789001234567890, 201234567890x-request-signature headerhttps://staging.cowpay.me/api/created_at, paid_at, completed_at timestamps