PayDirect

Webhooks API

Receive real-time notifications when payment statuses change. PayDirect sends HMAC-SHA256 signed POST requests to your registered endpoints. Stripe card payments use the same outbound events after fulfillment via POST /api/webhooks/stripe.

Stripe Inbound Webhooks (PayDirect ops)
Stripe → PayDirect. Configured in Stripe Dashboard, not via POST /api/v1/webhooks.

When a payer completes Stripe Checkout, Stripe notifies PayDirect at POST /api/webhooks/stripe. PayDirect verifies the signature, dedupes by event.id, and advances the unified payment lifecycle. Your merchant app then receives the usual outbound payment.forwarded event.

SettingValue
Endpoint URLhttps://www.paydirect.com/api/webhooks/stripe
API version2026-05-27.dahlia
Payload styleSnapshot
Stripe eventscheckout.session.completed, checkout.session.expired, payment_intent.payment_failed

Merchants do not configure this endpoint. Register POST /api/v1/webhooks on your own server for outbound PayDirect events.

Outbound Event Types
Subscribe via POST /api/v1/webhooks — same events for crypto and Stripe rails
EventDescription
payment.createdA new payment intent was created
payment.detectedAn incoming on-chain transfer was detected
payment.confirmedRequired block confirmations were met
payment.forwardedNet amount sent to merchant, fee sent to treasury
payment.failedSettlement failed due to an error
payment.expiredPayment expired without receiving funds
settlement.recordedA settlement was routed to the merchant, with its split breakdown (emitted only when the workspace has enabled splits)
split.recordedOne enabled split was carved at settlement — one event per split per settlement (includes settlementId, label, amount, percent_bps, destination, status, txRef, paymentIds)
POST
/api/v1/webhooks
Register a new webhook endpoint

Request Body

FieldTypeRequiredDescription
urlstringYesHTTPS endpoint that will receive POST requests
eventsstring[]YesNon-empty array of event types to subscribe to

Example

curl -X POST https://www.paydirect.com/api/v1/webhooks \
  -H "Authorization: Bearer pd_test_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/paydirect",
    "events": ["payment.confirmed", "payment.forwarded", "payment.failed"]
  }'

Response (201 Created)

{
  "webhook": {
    "id": 7,
    "url": "https://yourapp.com/webhooks/paydirect",
    "events": ["payment.confirmed", "payment.forwarded", "payment.failed"],
    "active": true,
    "created_at": "2026-02-16T14:36:22Z",
    "signingSecret": "whsec_a1b2c3d4e5f6..."
  }
}

The signingSecret is only returned once at creation. Store it securely for signature verification.

GET
/api/v1/webhooks
List all registered webhooks
curl https://www.paydirect.com/api/v1/webhooks \
  -H "Authorization: Bearer pd_test_abc123..."

Response (200 OK)

{
  "webhooks": [
    {
      "id": 7,
      "url": "https://yourapp.com/webhooks/paydirect",
      "events": ["payment.confirmed", "payment.forwarded", "payment.failed"],
      "active": true,
      "created_at": "2026-02-16T14:36:22Z"
    }
  ]
}
DELETE
/api/v1/webhooks/:id
Remove a webhook endpoint
curl -X DELETE https://www.paydirect.com/api/v1/webhooks/7 \
  -H "Authorization: Bearer pd_test_abc123..."

Response (200 OK)

{ "success": true }
Webhook Payload Format
What your endpoint receives on each event

HTTP Headers

HeaderDescription
X-PayDirect-SignatureHMAC-SHA256 hex digest of the request body
X-PayDirect-EventEvent type (e.g. payment.forwarded)
X-PayDirect-DeliveryUnique delivery ID for deduplication

Example Payload

{
  "event": "payment.forwarded",
  "data": {
    "id": "a1b2c3d4-...",
    "status": "forwarded",
    "tokenSymbol": "USDC",
    "grossAmount": "100.00",
    "feeAmount": "1.50",
    "netAmount": "98.50",
    "merchantWallet": "0xYourBaseAddress...",
    "paymentProvider": "onchain",
    "environment": "live",
    "createdAt": "2026-06-19T05:10:00Z"
  }
}

All payment fields are camelCase. The event name is in event and the payment object is in data.

Signature Verification
Always verify the HMAC-SHA256 signature before processing webhooks

TypeScript (using SDK)

import { PayDirectClient } from "@paydirectv2/sdk";

app.post("/webhooks/paydirect", (req, res) => {
  const signature = req.headers["x-paydirect-signature"];
  const body = JSON.stringify(req.body);

  const isValid = PayDirectClient.verifyWebhookSignature(
    process.env.WEBHOOK_SIGNING_SECRET,  // "whsec_..."
    body,
    signature
  );

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = req.headers["x-paydirect-event"];
  if (event === "payment.forwarded") {
    // Fulfill the order
  }

  res.status(200).json({ received: true });
});

Python (using SDK) — install from GitHub source (PyPI release coming)

from paydirect.webhooks import verify_signature
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/webhooks/paydirect", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-PayDirect-Signature")
    body = request.get_data(as_text=True)

    if not verify_signature(
        secret=WEBHOOK_SIGNING_SECRET,
        payload=body,
        signature=signature
    ):
        return jsonify({"error": "Invalid signature"}), 401

    event = request.headers.get("X-PayDirect-Event")
    if event == "payment.forwarded":
        # Fulfill the order
        pass

    return jsonify({"received": True}), 200

Raw Node.js (no SDK)

import crypto from "crypto";

function verifyWebhook(secret, body, signature) {
const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(signature, "hex")
  );
}
Best Practices
  • Always verify the X-PayDirect-Signature header before processing any webhook payload.
  • Use the X-PayDirect-Delivery header for idempotent processing to handle retries safely.
  • Return a 200 status quickly. Process heavy work asynchronously.
  • PayDirect retries failed deliveries (non-2xx responses) with exponential backoff.
  • Subscribe only to the events you need. Use payment.forwarded as the primary signal for order fulfillment.