Skip to main content
After a customer completes a payment, Quidkey delivers the result to your backend via an HTTPS webhook. This guide covers webhook setup, signature verification, fee processing, and a production QA checklist.

Redirect Handling

After bank authentication, the customer is redirected to the URL you specified when creating the payment request:
  • Success — redirected to success_url
  • Failure / Cancel — redirected to failure_url
Do not rely solely on the redirect to confirm payment. Always verify payment status via the webhook — redirects can fail or be interrupted.

Webhook Setup

Register and Obtain a Signing Secret

1

Register your webhook URL

curl -X POST 'https://core.quidkey.com/api/v1/webhooks' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "webhook_url": "https://api.yoursite.com/webhooks/quidkey"
  }'
The response confirms the URL has been registered.
2

Generate the signing secret

curl -X POST 'https://core.quidkey.com/api/v1/webhooks/secret' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
The secret is returned once — store it safely in a secure vault (AWS Secrets Manager, HashiCorp Vault, etc.).
See the Generate Webhook Secret API for complete details and interactive playground.
3

Revoke the secret (if needed)

Roll or revoke the secret during incident response:
curl -X POST 'https://core.quidkey.com/api/v1/webhooks/secret/revoke' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'

Webhook Payload

Quidkey sends a Stripe-style envelope so existing tooling can be reused.
{
  "id": "evt_28b2d68f",
  "object": "event",
  "created": 1716148300,
  "type": "quidkey.payment_request.succeeded",
  "data": {
    "object": {
      "id": "pr_782516093",
      "amount": 2550,
      "currency": "EUR",
      "status": "succeeded",
      "test": true,
      "metadata": {
        "order_id": "ORD-123",
        "payment_token": "ptok_..."
      },
      "fees": {
        "total_fees": 2.50,
        "fees_currency": "EUR",
        "fees_breakdown": [
          {
            "id": "fee_percentage_123",
            "type": "percentage",
            "amount": 1.50,
            "currency": "EUR",
            "rate_type": "domestic_percent_fee",
            "rate_value": 1.5,
            "notes": "1.5% fee on 25.50 EUR"
          }
        ]
      }
    }
  }
}
Fee information is only included for successful payments (status: "succeeded"). Failed or cancelled payments do not include fees.

HTTP Headers

HeaderPurpose
X-Signaturet=<unix-ts>,v1=<hex-hmac>
X-TimestampUnix epoch seconds
X-Client-IdYour client_id
The HMAC is SHA-256 over "${timestamp}.${raw_body}", keyed by your webhook signing secret.

Verify Signatures

The X-Signature header lets you confirm that the webhook came from Quidkey and that the payload was not tampered with.
const sig = req.get('x-signature');
const event = stripe.webhooks.constructEvent(
  req.rawBody,
  sig,
  process.env.QUIDKEY_WEBHOOK_SECRET
);

Process Webhook Events

app.post('/webhooks/quidkey', (req, res) => {
  const event = stripe.webhooks.constructEvent(
    req.body,
    req.headers['x-signature'],
    webhookSecret
  );

  if (event.type === 'quidkey.payment_request.succeeded') {
    const payment = event.data.object;

    // Store payment details
    await updatePayment(payment.metadata.order_id, {
      status: payment.status,
      amount: payment.amount,
      currency: payment.currency
    });

    // Process fee information if present
    if (payment.fees) {
      await storeFeeInformation({
        orderId: payment.metadata.order_id,
        totalFees: payment.fees.total_fees,
        feesCurrency: payment.fees.fees_currency,
        feeBreakdown: payment.fees.fees_breakdown
      });
    }

    // Process reward information if present
    if (payment.rewards) {
      await distributeRewards({
        orderId: payment.metadata.order_id,
        extraRewards: payment.rewards.extra_rewards,
        totalRewards: payment.rewards.total_rewards
      });
    }
  }

  res.status(200).send('OK');
});
Retry behavior: Quidkey retries for up to 3 days with exponential backoff until your endpoint returns any 2xx status. Events may be delivered out of order or duplicated — de-duplicate using the top-level id field.

Fee Handling

Quidkey automatically calculates and applies fees for successful transactions. Fee information is included in webhook payloads for merchant accounting and billing reconciliation. Fee types:
  • Percentage fees — based on transaction amount (e.g., 1.5% of €100 = €1.50)
  • Fixed fees — flat rate per transaction (e.g., €1.00 per transaction)
  • Currency-specific — fees are calculated in the same currency as the transaction
Each fee in the fees_breakdown array contains:
{
  "id": "fee_unique_identifier",
  "type": "percentage",
  "amount": 1.50,
  "currency": "EUR",
  "rate_type": "domestic_percent_fee",
  "rate_value": 1.5,
  "notes": "1.5% fee on 25.50 EUR"
}

QA Checklist Before Going Live

  • Serve checkout over HTTPS (wallets such as Apple Pay require it)
  • Use live Stripe keys in production mode (if using Stripe alongside)
  • Ask Quidkey for your production merchant_id and iframe URL
  • Store webhook secret in secure vault
  • Verify that only one payment method can be selected at any time
  • Confirm purchase button enables/disables correctly with bank selection
  • Test post-purchase redirect flows for success and failure
  • Verify dynamic height adjustments work smoothly
  • Verify amount updates work before payment initiation
  • Confirm updates are blocked after customer selects a bank
  • Test error handling for expired tokens
  • Ensure iframe refreshes correctly after updates
  • Verify webhook signature validation works
  • Test webhook retry and idempotency handling
  • Verify successful payments include fee information
  • Ensure your system correctly stores fee breakdown data

Next Steps