AmalQ icon
AmalQ
شفاف اسلامی عطیات
ابھی دیں

HajiQ Pay

Developer API

Integration Guide

Accept payments on your platform with our secure, reliable payment gateway.

https://api.amalq.org/api/v1/hajiq-pay/external

Before You Start

Essential requirements for API access

Required

What You'll Receive

  • API Keyhqp_live_yourproject_xxxxxxxx
  • Webhook Secretwhsec_xxxxxxxx
  • Stripe Publishable Keypk_live_xxxxxxxx

What You Must Provide

Webhook URL is mandatory

You must provide your webhook endpoint URL when requesting API keys. This is where we send payment status notifications.

https://yoursite.com/webhooks/hajiq-pay

Contactsupport@amalq.orgto request your credentials

Architecture

How payments flow through the system

Customer
Your Frontend
Your Backend
HajiQ Pay

About Frontend Card Collection

For PCI compliance, card details are collected directly via Stripe Elements on your frontend. Your backend creates payment intents through HajiQ Pay, and we handle the Stripe integration for you. You'll receive a Stripe publishable key during onboarding.

Payment Flow

1Customer clicks Pay
2Your backend calls HajiQ Pay API
3Return clientSecret to frontend
4Collect card via Stripe Elements
5Confirm payment with Stripe
6Payment processed
7HajiQ Pay sends webhook
8Fulfill order

Getting Started

Authenticate your API requests

Authentication

Every request must include your API key in the header:

Option 1X-API-Key: hqp_live_xxx
Option 2Authorization: Bearer hqp_live_xxx

Amount Format

Amounts are in cents. $50.00 = 5000

Min: $1.00 (100) • Max: $100,000 (10,000,000)

Currencies

USDGBPEURAEDSAR

API Reference

Click any endpoint to see request and response details

Code Examples

Production-ready integration snippets

Backend: Create Payment Intent

server.js
javascript
1// Create a payment intent on your server
2const response = await fetch('https://api.amalq.org/api/v1/hajiq-pay/external/payment-intents', {
3 method: 'POST',
4 headers: {
5 'X-API-Key': process.env.HAJIQ_PAY_API_KEY,
6 'Content-Type': 'application/json',
7 },
8 body: JSON.stringify({
9 amount: 5000, // $50.00 in cents
10 currency: 'usd',
11 externalReferenceId: 'ORDER-12345',
12 customerEmail: 'customer@example.com',
13 customerName: 'John Doe',
14 description: 'Purchase from My Store',
15 }),
16});
17
18const { data } = await response.json();
19
20// Return clientSecret to your frontend
21return { clientSecret: data.clientSecret };

Frontend: Stripe Elements Integration

Note: Use @stripe/stripe-js and @stripe/react-stripe-js for React projects. The clientSecret from HajiQ Pay works directly with Stripe's payment confirmation.

Checkout.jsx
jsx
1// Frontend: Use Stripe Elements for PCI-compliant card collection
2// The clientSecret from HajiQ Pay works directly with Stripe Elements
3import { loadStripe } from '@stripe/stripe-js';
4import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
5
6// Initialize Stripe with YOUR Stripe publishable key
7// (provided by HajiQ Pay during onboarding)
8const stripePromise = loadStripe('pk_live_your_publishable_key');
9
10function CheckoutForm() {
11 const stripe = useStripe();
12 const elements = useElements();
13
14 const handleSubmit = async (e) => {
15 e.preventDefault();
16
17 // 1. Get clientSecret from YOUR backend (which calls HajiQ Pay)
18 const { clientSecret } = await fetch('/api/create-payment', {
19 method: 'POST',
20 headers: { 'Content-Type': 'application/json' },
21 body: JSON.stringify({ orderId: 'ORDER-12345', amount: 5000 }),
22 }).then(res => res.json());
23
24 // 2. Confirm payment with Stripe Elements
25 const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
26 payment_method: {
27 card: elements.getElement(CardElement),
28 billing_details: { name: 'John Doe' },
29 },
30 });
31
32 if (error) {
33 console.error(error.message);
34 } else if (paymentIntent.status === 'succeeded') {
35 // 3. HajiQ Pay receives webhook, notifies YOUR server
36 window.location.href = '/order-confirmed';
37 }
38 };
39
40 return (
41 <form onSubmit={handleSubmit}>
42 <CardElement options={{ style: { base: { fontSize: '16px' } } }} />
43 <button type="submit">Pay Now</button>
44 </form>
45 );
46}
47
48// Wrap your checkout with Stripe Elements provider
49export default function Checkout() {
50 return (
51 <Elements stripe={stripePromise}>
52 <CheckoutForm />
53 </Elements>
54 );
55}

Payment Statuses

Track payments through their lifecycle

pending

Waiting for customer

processing

Card submitted

succeeded

Payment complete

failed

Card declined

cancelled

Cancelled by you

refunded

Money returned

Webhooks

Receive real-time payment notifications

payment.succeeded

Payment completed successfully – fulfill the order!

payment.failed

Card declined or error occurred

payment.cancelled

Payment was cancelled

Webhook Payload Example

Webhook Body
json
{
"id": "evt_xxxxxxxxxxxx",
"type": "payment.succeeded",
"created": 1703244600,
"data": {
"object": {
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"status": "succeeded",
"amount": 5000,
"currency": "USD",
"externalReferenceId": "ORDER-12345",
"customerEmail": "customer@example.com",
"customerName": "John Doe",
"completedAt": "2025-12-22T10:35:00.000Z"
}
}
}

Webhook Headers

X-HajiQ-SignatureSignature to verify authenticity
X-HajiQ-Delivery-IDUnique delivery ID
X-HajiQ-Event-TypeEvent type
Content-Typeapplication/json

Webhook Verification

webhook-handler.js
javascript
1const crypto = require('crypto');
2
3function verifyWebhook(payload, signature, secret) {
4 // Parse the signature header
5 const parts = signature.split(',');
6 let timestamp, sig;
7
8 for (const part of parts) {
9 const [key, value] = part.split('=');
10 if (key === 't') timestamp = value;
11 if (key === 'v1') sig = value;
12 }
13
14 // Check timestamp is recent (within 5 minutes)
15 const now = Math.floor(Date.now() / 1000);
16 if (Math.abs(now - parseInt(timestamp)) > 300) {
17 return false; // Too old, possible replay attack
18 }
19
20 // Calculate expected signature
21 const signedPayload = `${timestamp}.${payload}`;
22 const expectedSig = crypto
23 .createHmac('sha256', secret)
24 .update(signedPayload)
25 .digest('hex');
26
27 // Compare signatures (use timing-safe comparison)
28 return crypto.timingSafeEqual(
29 Buffer.from(sig),
30 Buffer.from(expectedSig)
31 );
32}
33
34// In your webhook endpoint
35app.post('/webhooks/hajiq-pay', (req, res) => {
36 const signature = req.headers['x-hajiq-signature'];
37 const payload = JSON.stringify(req.body);
38
39 if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
40 return res.status(400).send('Invalid signature');
41 }
42
43 const event = req.body;
44
45 switch (event.type) {
46 case 'payment.succeeded':
47 const orderId = event.data.object.externalReferenceId;
48 fulfillOrder(orderId);
49 break;
50 case 'payment.failed':
51 // Handle failed payment
52 break;
53 }
54
55 res.status(200).send('OK');
56});

Retry Schedule

If your server doesn't respond with 200, we retry:

#1
Immediate
#2
1 min
#3
5 min
#4
15 min
#5
1 hour
#6
4 hours

After 6 failed attempts, we stop trying.

Security

Keep your integration secure

Card Data Isolation

Card numbers go directly to Stripe via Elements. Never touches your servers.

Webhook Verification

Always verify X-HajiQ-Signature using HMAC-SHA256.

API Key Security

Never expose your API key in frontend code. Use environment variables.

Replay Protection

Reject webhook timestamps older than 5 minutes.

Rate Limits

1,000
requests per hour per API key
10
failed auth attempts per 15 minutes

If you hit the limit, you'll receive a 429 Rate Limited response.

Error Codes

Handle errors gracefully

Error Response Format

{
  "success": false,
  "error": { "code": "ERROR_CODE", "message": "..." }
}
CodeHTTPMeaning
MISSING_API_KEY401No API key provided
INVALID_API_KEY401API key is wrong
API_KEY_EXPIRED401API key has expired
API_KEY_REVOKED401API key was revoked
INSUFFICIENT_SCOPE403API key doesn't have permission
RATE_LIMITED429Too many requests
VALIDATION_ERROR400Request data is invalid
INVALID_AMOUNT400Amount is out of range
UNSUPPORTED_CURRENCY400Currency not supported
PAYMENT_NOT_FOUND404Payment doesn't exist
PAYMENT_CANNOT_BE_CANCELLED400Payment already completed
PAYMENT_NOT_REFUNDABLE400Payment can't be refunded
INVALID_REFUND_AMOUNT400Refund amount is wrong
REFUND_NOT_FOUND404Refund doesn't exist
INTERNAL_ERROR500Something went wrong on our end
HajiQ PayAPI v1.0

Base URL: https://api.amalq.org/api/v1/hajiq-pay/external

Last updated: December 23, 2025 • 14:32 UTC