Payment Webhook

HTTP Method: POST
Endpoint URL: https://your-server.com/webhooks/payment (configured in your ALPPAY settings)
Description: ALPPAY sends this webhook to notify your server about payment creation or status changes.
Security: Verify the X-HMAC header to ensure the request is from ALPPAY.

Webhook Configuration

To receive payment webhooks:

  1. Contact [email protected] with your webhook endpoint URL
  2. Ensure your endpoint accepts POST requests
  3. Implement HMAC signature verification for security
  4. Respond with HTTP 200 OK to acknowledge receipt

Request Headers

  • Content-Type: application/json
  • X-HMAC: signature_string — HMAC-SHA256 signature of the request body

Request Body (JSON)

{
  "id": "285d8dce-7663-4580-ba7f-8afb2f2d3292",
  "transactions": [
    {
      "paymentId": "285d8dce-7663-4580-ba7f-8afb2f2d3292",
      "txnId": "0xe7238caa68382485141be0443d6ba7efd0bd9f6bac5a624bd059acc53af1bf1d19",
      "receivedAmount": "20.00",
      "asset": {
        "short": "USDT",
        "name": "Tether",
        "logoUrl": "https://cryptologos.cc/logos/tether-usdt-logo.png",
        "network": "tron"
      },
      "destTag": null,
      "addressFrom": "TXkFpL3HmzE9KPpXGWNXdCbNcDYYn73U4C",
      "addressTo": "TPSMckmxoQBQWfUcasbUs2cRUdh2EMQu4n",
      "date": "2025-05-20T12:34:56Z"
    }
  ],
  "amount": "1000.00",
  "totalReceivedAmount": "20.00",
  "asset": {
    "short": "USDT",
    "name": "Tether",
    "logoUrl": "https://cryptologos.cc/logos/tether-usdt-logo.png",
    "network": "tron"
  },
  "addressTo": "TPSMckmxoQBQWfUcasbUs2cRUdh2EMQu4n",
  "confirmsNeeded": 10,
  "timeout": 9000,
  "checkoutUrl": "https://your-shop.com/orders/40113049",
  "status": "OPEN",
  "customer": {
    "id": 12312312,
    "name": "John",
    "email": "[email protected]"
  },
  "merchant": {
    "id": "285d8dce-7663-4580-ba7f-8afb2f2d3292",
    "name": "Your Company",
    "siteUrl": "https://your-shop.com",
    "logoUrl": "https://your-shop.com/logo.svg",
    "email": "[email protected]"
  },
  "invoice": "INV-001",
  "paymentRedirectUrl": "https://a-pay-system.com/en/invoice?id=285d8dce-7663-4580-ba7f-8afb2f2d3292",
  "createdAt": "2025-05-20T12:34:56Z",
  "updatedAt": "2025-05-20T12:34:56Z"
}

Field Descriptions

  • id — Unique payment identifier
  • transactions — Array of blockchain transactions:
    • paymentId — Reference to parent payment ID
    • txnId — Blockchain transaction hash
    • receivedAmount — Amount received in this transaction
    • asset — Cryptocurrency details
    • destTag — Destination tag if applicable
    • addressFrom — Sender's wallet address
    • addressTo — Receiving wallet address
    • date — Transaction timestamp
  • amount — Requested payment amount
  • totalReceivedAmount — Sum of all received transactions
  • asset — Primary payment cryptocurrency details
  • addressTo — Payment receiving address
  • confirmsNeeded — Required blockchain confirmations
  • timeout — Payment expiration time in seconds
  • checkoutUrl — Merchant's return URL
  • status — Current payment status
  • customer — Customer information
  • merchant — Merchant account details
  • invoice — Merchant's invoice reference
  • paymentRedirectUrl — ALPPAY payment page URL
  • createdAt — Payment creation timestamp
  • updatedAt — Last update timestamp

Webhook Events and Status Changes

ALPPAY sends webhooks for the following payment events:

1. Payment Created

Sent when a new payment is created via API.

  • transactions array will be empty
  • totalReceivedAmount will be "0.00"
  • status is "OPEN"

2. Payment Partially Paid

Sent when payment receives funds but less than requested amount.

  • transactions array contains received transaction(s)
  • totalReceivedAmount < amount
  • status is "OPEN"

3. Payment Completed

Sent when payment receives full requested amount.

  • transactions array contains received transaction(s)
  • totalReceivedAmount >= amount
  • status is "OPEN"

4. Payment Overpaid

Sent when payment receives more than requested amount.

  • transactions array contains received transaction(s)
  • totalReceivedAmount > amount
  • status is "OPEN"

5. Payment Expired

Sent when payment timeout is reached without full payment.

  • No new transactions will be accepted
  • status is "EXPIRED"

Required Response

Your server must respond with:

  • HTTP Status: 200 OK
  • Body: Empty or simple acknowledgment

Example successful response:

HTTP/1.1 200 OK
Content-Length: 0

HMAC Signature Verification

Always verify the webhook authenticity using the X-HMAC header:

Node.js Example

import crypto from 'crypto';

function verifyWebhook(body, signature, secretKey) {
  const expectedSignature = crypto
    .createHmac('sha256', secretKey)
    .update(JSON.stringify(body), 'utf8')
    .digest('hex');
  
  return signature === expectedSignature;
}

// In your webhook handler
import crypto from 'crypto';

function verifyWebhook(body, signature, secretKey) {
  const expectedSignature = crypto
    .createHmac('sha256', secretKey)
    .update(JSON.stringify(body), 'utf8')
    .digest('hex');
  
  return signature === expectedSignature;
}

app.post('/webhooks/payment', (req, res) => {
  const signature = req.headers['x-hmac'];
  const isValid = verifyWebhook(req.body, signature, process.env.HMAC_SECRET);
  
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }
  
  const payment = req.body;
  
  if (payment.status === 'EXPIRED') {
    // Handle expired payment
    console.log('Payment expired:', payment.invoice);
  } else if (payment.status === 'OPEN') {
    // Determine payment state based on received amount
    if (payment.totalReceivedAmount === '0.00') {
      console.log('Payment created:', payment.invoice);
    } else if (payment.totalReceivedAmount < payment.amount) {
      console.log('Payment partially paid:', payment.invoice);
    } else if (payment.totalReceivedAmount === payment.amount) {
      console.log('Payment completed:', payment.invoice);
    } else if (payment.totalReceivedAmount > payment.amount) {
      console.log('Payment overpaid:', payment.invoice);
    }
  }
  
  res.status(200).send();
});

Handling Partial Payments

When a payment is partially paid, you'll receive a webhook with:

  • Status remains OPEN
  • totalReceivedAmount shows the current total
  • transactions array contains all received transactions

Example partial payment handling:

if (payment.status === 'OPEN' && parseFloat(payment.totalReceivedAmount) > 0) {
  const percentPaid = (parseFloat(payment.totalReceivedAmount) / parseFloat(payment.amount)) * 100;
  console.log(`Payment ${percentPaid.toFixed(2)}% complete`);
  
  // Notify customer about partial payment
  sendPartialPaymentNotification(payment.customer.email, payment.totalReceivedAmount, payment.amount);
}

Best Practices

  1. Always verify HMAC signature before processing
  2. Respond quickly (within 5 seconds) to avoid timeouts
  3. Use idempotent processing - same webhook may be sent multiple times
  4. Handle all status types including partial payments
  5. Store payment state to track payment progression
  6. Log all webhooks for debugging and reconciliation
  7. Implement proper error handling for webhook processing failures

Testing Webhooks

For testing webhook integration:

  1. Use ngrok or similar service to expose local endpoints
  2. Create test payments in sandbox environment
  3. Monitor webhook delivery in ALPPAY dashboard
  4. Test all status scenarios including partial payments