Skip to main content
ToffeePay uses webhooks to notify your backend when important events occur, such as successful payments, completed refunds or successful wallet deposits. All webhooks are signed using Standard Webhooks.

Webhook Structure

All ToffeePay webhooks follow a consistent structure:
{
  "type": "session.created",
  "timestamp": "2023-06-01T12:05:00Z",
  "data": {
    // Event-specific data
  }
}
Fields:
  • type: Event type (e.g., session.created, payment.succeeded, deposit.succeeded)
  • timestamp: ISO 8601 timestamp when the event occurred
  • data: Event-specific payload containing the relevant entity data

Webhook Events

Payment Events

EventDescription
session.createdA payment session was created
session.paidA session was successfully paid
session.failedA payment attempt related to a session failed
session.expiredA session expired without payment
session.cancelledA session was cancelled
payment.createdA payment was created
payment.succeededA payment was successfully processed
payment.failedA payment attempt failed
payment.authorizedA payment was authorized for later capture
payment.cancelledA payment was cancelled
refund.createdA refund was created
refund.succeededA refund was processed successfully
refund.failedA refund attempt failed

Wallet Events

EventDescription
deposit.succeededA wallet deposit completed successfully
deposit.failedA wallet deposit failed

Webhook Signature Verification

All webhooks are signed using Standard Webhooks. Each request includes these headers:
HeaderDescription
webhook-idUnique message identifier for idempotency
webhook-timestampUnix timestamp of the event
webhook-signatureSignature in format v1,{base64-encoded-signature}

Verification

Use the official Standard Webhooks libraries to verify webhook signatures:
import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.WEBHOOK_SECRET);

function handleWebhook(req: Request) {
  const payload = wh.verify(req.body, req.headers);

  switch (payload.type) {
    case 'session.paid':
      handleSessionPaid(payload.data);
      break;
    case 'payment.succeeded':
      handlePaymentSuccess(payload.data);
      break;
    case 'refund.succeeded':
      handleRefundSuccess(payload.data);
      break;
    case 'deposit.succeeded':
      handleDepositSuccess(payload.data);
      break;
  }
}

Idempotency

Webhooks may be delivered more than once. Use the webhook-id header to deduplicate:
const webhookId = req.headers['webhook-id'];

if (await isProcessed(webhookId)) {
  return; // Skip duplicate
}

// ... verify and process

await markProcessed(webhookId);

Webhook Configuration

You can configure and manage your webhook settings:
  1. Webhook URL: The endpoint where ToffeePay will send webhooks
  2. Webhook Secret: Used to sign webhook payloads (can be rotated)
  3. Event Selection: Choose which events to receive (payments, refunds, deposits, or all)

Best Practices

  1. Always verify signatures: Never process webhooks without signature verification
  2. Handle duplicate events: While rare, webhooks may be sent multiple times — use the webhook-id header for idempotency
  3. Respond quickly: Return a 200 status code within 10 seconds to acknowledge receipt
  4. Retry logic: ToffeePay will retry failed webhooks, but implement your own retry logic for critical operations
  5. Log webhook events: Keep audit logs of all webhook events for debugging and compliance
  6. Use HTTPS: Always use HTTPS endpoints for webhook URLs

Troubleshooting

Common Issues

  • Signature verification fails: Check that you’re using the correct webhook secret and following the verification steps exactly
  • Timeout errors: Ensure your webhook endpoint responds within 10 seconds
  • Missing webhooks: Check your webhook URL configuration and ensure your endpoint is accessible

Testing Webhooks

Use tools like ngrok for local development to expose your local webhook endpoint to ToffeePay:
ngrok http 3000