REST API

Shulker Payment Gateway v1.0.0

Programmatically create payment links, track status, and handle webhooks. Built for startups & merchants — one API to rule them all.

Updated recently 20 min read Production ready

Introduction

The Shulker Payment Gateway API lets you generate payment requests, track their status, and receive webhook notifications when payments succeed. It powers the branded checkout page at /payment-gateway/ and handles all the heavy lifting.

Branded checkout
Fully customisable with your business colours and logo.
Rich analytics
Track payer location, device, browser, and conversion funnels.
Webhooks
Real‑time payment notifications with HMAC signature verification.
Base URL
https://shulker.in/api/shulker-pg-v1.0/

Authentication

All endpoints except ping, log_event, and complete_payment require an API key. Pass it via the X-API-Key header, the Authorization: Bearer header, or the api_key query parameter.

Header (recommended)
X-API-Key: spgk_your_secret_key_here
Bearer
Authorization: Bearer spgk_your_secret_key_here

🔐 Keep it secret — API keys grant full access to your payment requests. Never expose them in client‑side code. Rotate regularly.

Rate limits

Per‑API‑key limits are soft and can be raised on request.

60 / minute
Per API key
1,000 / hour
Per API key
10,000 / day
Per API key
Rate‑limit headers
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1697385645

Error handling

StatusMeaning
200Success
400Bad request — invalid parameter
401Missing or invalid API key
403Forbidden — insufficient permissions
404Resource not found
409Conflict — already processed
410Gone — payment request expired
422Unprocessable entity — validation failed
429Too many requests — rate limit
500Internal server error
Error response
{
    "success": false,
    "message": "amount_usd must be between 0.01 and 10000.00"
}

Create payment request

Generates a new payment link. The request is valid for 24 hours.

POST requires API key
?action=create_request
ParameterTypeRequiredDescription
amount_usdfloat✅Amount in USD (min 0.01, max depends on key)
redirect_urlstring✅Valid http/https URL for post‑payment redirection
descriptionstring✅Max 500 chars, shown on checkout
currency_overridestring❌USD, INR, GBP, EUR, CAD, AUD (defaults to payer's geo)
metadataobject❌Up to 20 keys, stored for webhook use
cURL
curl -X POST https://shulker.in/api/shulker-pg-v1.0/ \
  -H "X-API-Key: spgk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "action": "create_request",
    "amount_usd": 49.99,
    "redirect_url": "https://myapp.com/success",
    "description": "Pro plan subscription",
    "metadata": { "user_id": 12345 }
  }'
Response
{
    "success": true,
    "message": "Payment request created.",
    "data": {
        "payment_request_id": "SPGR_A1B2C3D4E5F6",
        "checkout_url": "https://shulker.in/payment-gateway/?payment_request=SPGR_A1B2C3D4E5F6",
        "amount_usd": 49.99,
        "amount_inr": 4699.06,
        "expires_at": "2026-09-09 16:30:00",
        "status": "pending"
    }
}

Get payment status

GET requires API key
?action=get_status&payment_request_id=SPGR_...
cURL
curl "https://shulker.in/api/shulker-pg-v1.0/?action=get_status&payment_request_id=SPGR_A1B2C3&api_key=spgk_..."
Response
{
    "success": true,
    "data": {
        "payment_request_id": "SPGR_A1B2C3",
        "status": "completed",
        "amount_usd": 49.99,
        "payer": {
            "name": "John Doe",
            "email": "[email protected]",
            "country_code": "IN"
        },
        "txn_id": "pay_xyz",
        "completed_at": "2026-09-09 14:20:00"
    }
}

List payment requests

GET requires API key
?action=list_requests&page=1&limit=20&status=pending

📄 Pagination — page and limit (max 100) are optional. Filter by status: pending, completed, expired, failed.

Manually expire a request

POST requires API key
?action=expire_request
cURL
curl -X POST https://shulker.in/api/shulker-pg-v1.0/ \
  -H "X-API-Key: spgk_..." \
  -d "action=expire_request&payment_request_id=SPGR_A1B2C3"

Log analytics event

Internal endpoint used by the checkout page to track user behaviour. No API key required — uses the payment request ID as a token.

POST public (token)
?action=log_event
ParameterDescription
payment_request_idRequired
event_typepage_view, form_started, form_completed, payment_initiated, payment_success, payment_failed, payment_cancelled
payloadOptional JSON object

Complete payment

Called by the checkout page after a successful Razorpay payment. No API key required — uses a checkout token for authorisation.

POST public (token)
?action=complete_payment

✅ Auto‑webhook — after completion, a webhook is fired to the merchant’s configured URL with the full payment payload.

Checkout flow (frontend)

  1. Create a payment request via the API → get checkout_url.
  2. Redirect the user to that URL (or embed it).
  3. The checkout page automatically detects the user’s country & currency (via ipapi.co) and shows the branded form.
  4. User fills in name, email, phone → selects payment method (UPI / card / net banking / wallet).
  5. Razorpay modal opens → user completes payment.
  6. On success, the page calls complete_payment → logs data, fires webhook, redirects to your redirect_url.

💡 Customisation — all colours, logo, and business name are pulled from the API key record. Update them via the dashboard.

Webhooks

When a payment is completed, Shulker sends a POST request to your configured webhook_url with a JSON payload and a X-SPG-Signature header (HMAC‑SHA256 of the raw body using your webhook_secret).

Webhook payload
{
    "event": "payment.completed",
    "payment_request_id": "SPGR_A1B2C3",
    "amount_usd": 49.99,
    "currency": "INR",
    "display_amount": 4699.06,
    "payment_method": "card",
    "txn_id": "pay_xyz",
    "payer": {
        "name": "John Doe",
        "email": "[email protected]",
        "country": "IN"
    },
    "metadata": { "user_id": 12345 },
    "completed_at": "2026-09-09T14:20:00+00:00"
}

🔐 Verify signatures — always compute hash_hmac('sha256', raw_body, your_secret) and compare with the X-SPG-Signature header to avoid forged calls.

Best practices

🔐 Security — store API keys in environment variables, never in version control. Use the webhook_secret to verify signatures. Rotate keys every 90 days.

⚡ Performance — cache API key details if you make many requests. Use list_requests with pagination for bulk operations.

🔄 Error handling — implement exponential backoff for 5xx errors. Store payment_request_id and txn_id for reconciliation.

📊 Analytics — leverage the built‑in analytics tables (shulker_pg_analytics_events) to understand your payer funnel.