Kutanapay Merchant API
    Kutanapay Merchant API
    • Merchant API Keys
      • Create Api Key
        POST
      • Get Api Keys
        GET
      • Revoke Api Key
        DELETE
    • Merchant Wallet
      • Get All Wallets
        GET
      • Get Wallet Transactions
        GET
      • Process Refund
        POST
      • Get Settlement Batches
        GET
      • Update Wallet Status
        PATCH
      • Get Wallet By Currency
        GET
    • Merchant Customer Transactions
      • Get Merchant Transactions
        GET
      • Get Merchant Transaction
        GET
    • Merchant Checkout
      • Create Checkout
        POST
      • List Checkouts
        GET
      • Get Checkout
        GET
    • Merchant Webhooks
      • Create Webhook
        POST
      • Get Webhooks
        GET
      • Update Webhook
        PUT
      • Delete Webhook
        DELETE
    • Merchant Branding
      • Get Branding
        GET
      • Update Branding
        PUT
    • Merchant Payment Accounts
      • Add Payment Account
        POST
      • Get Payment Accounts
        GET
      • Get Payment Account
        GET
      • Update Payment Account
        PUT
      • Delete Payment Account
        DELETE
    • Merchant Withdrawals
      • Initiate Withdrawal
      • Get Withdrawal History
      • Cancel Withdrawal
      • Get Withdrawal
    • Merchant Payouts
      • Initiate Payout
      • Get Payout History
      • Cancel Payout
      • Get Payout

    Merchant Webhooks

    Webhook Signature Verification Guide#

    Overview#

    KutanaPay signs all webhook requests with HMAC-SHA256 signatures to ensure authenticity and prevent tampering. You should always verify webhook signatures before processing webhook data.

    How It Works#

    1.
    KutanaPay generates a signature using your webhook's secret key
    2.
    The signature is sent in the X-Webhook-Signature header
    3.
    Your endpoint verifies the signature matches the payload
    4.
    If verification fails, reject the webhook

    Headers Sent#

    Every webhook request includes these headers:
    X-Webhook-Signature: sha256=<signature>
    X-Webhook-Event: checkout.created
    X-Webhook-Idempotency-Key: <uuid>
    User-Agent: KutanaPay-Webhook/1.0
    Content-Type: application/json

    Payload Structure#

    All webhooks follow this versioned structure:
    {
      "version": "v1",
      "event_type": "checkout.created",
      "timestamp": "2025-11-17T12:34:56Z",
      "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
      "merchant_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "data": {
        // Event-specific data (full object)
      }
    }

    Signature Verification#

    Python#

    Node.js#

    Security Best Practices#

    1. Always Verify Signatures#

    ❌ BAD:
    ✅ GOOD:

    2. Use Constant-Time Comparison#

    Prevents timing attacks. Always use:
    Python: hmac.compare_digest()
    Node.js: crypto.timingSafeEqual()
    PHP: hash_equals()
    Ruby: ActiveSupport::SecurityUtils.secure_compare()

    3. Validate Timestamp#

    Reject old webhooks to prevent replay attacks:

    4. Use Idempotency Keys#

    Store processed idempotency_key values to prevent duplicate processing:

    5. Return 2xx Quickly#

    Respond within your configured timeout (default 30s):

    6. Handle Retries Gracefully#

    KutanaPay retries failed webhooks (5min → 30min → 6hr):
    Make operations idempotent
    Return 2xx for already-processed webhooks
    Return 4xx for permanent failures (won't retry)
    Return 5xx for temporary failures (will retry)

    Event Types#

    Event TypeDescriptionWhen Triggered
    checkout.createdNew checkout session createdCustomer initiates payment
    checkout.paidCustomer marked checkout as paidCustomer confirms payment
    checkout.approvedAdmin approved paymentAdmin verifies payment
    checkout.rejectedAdmin rejected paymentAdmin rejects payment
    checkout.expiredCheckout expired without paymentTimeout (24h default)
    checkout.completedPayment fully processedFunds credited
    checkout.failedPayment processing failedPayment error
    deposit.receivedFunds deposited to walletAdmin deposit
    payout.processedPayout to third party completedPayout sent
    withdrawal.completedOwner withdrawal processedOwner withdraws funds
    settlement.completedT+1 settlement batch processedDaily settlement
    user.invitedTeam member invitedUser invite sent
    user.joinedTeam member accepted inviteUser joined team

    Testing Webhooks#

    Use the test endpoint in your dashboard:
    Response includes:
    Status code received
    Response body
    Latency
    Headers sent
    Full payload

    Troubleshooting#

    Signature Verification Fails#

    1.
    Check raw body: Don't parse JSON before verifying
    2.
    Check secret: Ensure you're using the correct secret from dashboard
    3.
    Check encoding: Use UTF-8 encoding consistently
    4.
    Check header name: Case-sensitive: X-Webhook-Signature

    Webhooks Not Received#

    1.
    Check URL accessibility: Ensure your endpoint is publicly accessible
    2.
    Check HTTPS: Production requires HTTPS
    3.
    Check firewall: Allow KutanaPay IPs (contact support for list)
    4.
    Check logs: View delivery attempts in dashboard

    Auto-Disabled After Failures#

    Your webhook was disabled after 10 consecutive failures:
    1.
    View logs: Check last 10 delivery attempts
    2.
    Test endpoint: Use test endpoint to diagnose
    3.
    Fix issues: Address errors in your endpoint
    4.
    Re-enable: Reactivate webhook in dashboard

    Rate Limits#

    Delivery: 100 requests/minute per merchant
    Config changes: 10 changes/hour per merchant
    Modified at 2025-11-19 14:59:22
    Previous
    Get Checkout
    Next
    Create Webhook
    Built with