Pay
Webhooks

Transaction Webhooks

Monitor payments, payouts, and transaction events in real-time


Transaction webhooks notify you instantly when payment and payout events occur. Receive alerts when transactions are processed, declined, refunded, or rejected, enabling you to automate order fulfillment, send customer notifications, and keep your systems synchronized with transaction status changes.

Prerequisites

Before implementing transaction webhooks, familiarize yourself with these topics.


Transaction-Specific Use Cases


Transaction webhooks enable real-time payment automation:

  • Order fulfillment - Automatically fulfill, hold, or cancel orders based on payment status
  • Customer notifications - Send instant confirmations, failure alerts, and refund notices
  • Payment reconciliation - Sync transaction data to accounting systems in real-time
  • Failed payment recovery - Retry declined payments and trigger dunning workflows

Transaction Webhook Triggers

Webhook triggers available for monitoring transaction and payment events.

Payment Lifecycle Events

TriggerWhen It FiresCommon Use Cases
paymentPayment transaction createdLog all payment attempts, fraud monitoring
processedTransaction completed successfullyFulfill orders, send receipts, update inventory
authorizedPayment authorized (not captured)Reserve inventory, verify orders before capture
declinePayment declined by processor/bankNotify customer, retry payment, update order status
adjustedTransaction amount adjustedUpdate accounting, notify customer of changes

Refund and Void Events

TriggerWhen It FiresCommon Use Cases
refundRefund transaction createdUpdate inventory, notify customer, reverse charges
voidTransaction voided before settlementCancel order, release inventory, notify customer

Rejection Events

TriggerWhen It FiresCommon Use Cases
rejectTransaction rejected after settlementHandle NSF, invalid accounts, retry payment

Payout Events

TriggerWhen It FiresCommon Use Cases
payoutPayout transaction createdTrack outgoing payments, notify recipients

Funding Events

TriggerWhen It FiresCommon Use Cases
depositDeposit transaction createdMonitor account funding, track cash flow
withdrawWithdraw transaction createdTrack outgoing funds, monitor cash outflows

Recurring Payment Events

TriggerWhen It FiresCommon Use Cases
automatic_paymentScheduled payment triggeredMonitor subscription billing, handle failures

Transaction Webhook Workflows

Common workflows for handling transaction events.

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/webhooks/transaction-processed", methods=["POST"])
def handle_processed():
    event = request.json
    trigger = event["trigger"]
    triggered_on = event["triggered_on"]
 
    if trigger == "processed":
        transaction = pl.Transaction.get(triggered_on["id"])
 
        fulfill_order(transaction.order_number)
        update_order_status(transaction.order_number, "paid")
        send_receipt(transaction)
        sync_to_accounting(transaction)
 
    return "OK", 200
from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/webhooks/transaction-declined", methods=["POST"])
def handle_decline():
    event = request.json
    trigger = event["trigger"]
    triggered_on = event["triggered_on"]
 
    if trigger == "decline":
        transaction = pl.Transaction.get(triggered_on["id"])
        decline_code = transaction.status["code"]
 
        send_decline_notification(transaction, decline_code)
 
        if can_retry(decline_code):
            retry_payment(transaction)
 
        update_order_status(transaction.order_number, "payment_failed")
        log_decline(transaction, decline_code)
 
    return "OK", 200
from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/webhooks/refund-issued", methods=["POST"])
def handle_refund():
    event = request.json
    trigger = event["trigger"]
    triggered_on = event["triggered_on"]
 
    if trigger == "refund":
        refund = pl.Transaction.get(triggered_on["id"])
        original_transaction = refund.parent_id
 
        restock_items(original_transaction.order_number)
        send_refund_confirmation(refund)
        update_order_status(original_transaction.order_number, "refunded")
        record_refund(refund)
 
    return "OK", 200
from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/webhooks/transaction-rejected", methods=["POST"])
def handle_reject():
    event = request.json
    trigger = event["trigger"]
    triggered_on = event["triggered_on"]
 
    if trigger == "reject":
        transaction = pl.Transaction.get(triggered_on["id"])
        reject_code = transaction.status["code"]
 
        if reject_code == "insufficient_bal":
            handle_nsf(transaction)
            notify_customer_nsf(transaction)
            schedule_retry(transaction, 7)
 
        if reject_code == "invalid_account_number":
            flag_payment_method(transaction.payment_method_id)
            request_new_payment_method(transaction)
 
        update_order_status(transaction.order_number, "payment_rejected")
 
    return "OK", 200
from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/webhooks/payment-authorized", methods=["POST"])
def handle_authorized():
    event = request.json
    trigger = event["trigger"]
    triggered_on = event["triggered_on"]
 
    if trigger == "authorized":
        transaction = pl.Transaction.get(triggered_on["id"])
 
        reserve_inventory(transaction.order_number)
        fraud_score = check_fraud(transaction)
 
        if fraud_score > THRESHOLD:
            pl.Transaction.void(transaction.id)
            notify_fraud(transaction)
        else:
            approve_for_capture(transaction.order_number)
 
    return "OK", 200

Setting Up Transaction Webhooks

Create webhooks for transaction events using the Webhooks API:

import payload
 
# Create a webhook for transaction processed events
webhook = payload.Webhook.create({
    'trigger': 'processed',
    'url': 'https://yourdomain.com/webhooks/transaction-processed'
})
 
print(f'Webhook ID: {webhook.id}')
print(f'Trigger: {webhook.trigger}')
print(f'URL: {webhook.url}')

For complete webhook setup and security, see:


Webhook Event Structure

Transaction webhooks POST a JSON payload with the following structure:

{
  "event_id": "evt_abc123xyz",
  "trigger": "processed",
  "triggered_on": {
    "id": "txn_123",
    "object": "transaction",
    "value": "processed"
  }
}

Event fields:

  • event_id - Unique identifier for this webhook event (use for idempotency)
  • trigger - Event type that triggered the webhook (processed, decline, refund, etc.)
  • triggered_on.id - ID of the transaction that triggered the event
  • triggered_on.object - Object type (always transaction for transaction webhooks)
  • triggered_on.value - Current status value of the transaction

Monitoring Transaction Webhooks

Monitor webhook delivery and debug issues using webhook logs:

import payload
 
# Retrieve webhook with logs
webhook = payload.Webhook.select('*', 'logs').get('wh_webhook123')
 
print(f'Webhook ID: {webhook.id}')
print(f'Trigger: {webhook.trigger}')
print(f'\nWebhook Logs ({len(webhook.logs)} entries):')
 
for log in webhook.logs:
    print(f'\nDate: {log.created_at}')
    print(f'Trigger: {log.trigger}')
    print(f'HTTP Status: {log.http_status}')
    print(f'Triggered on: {log.triggered_on["id"]}')
    print(f'URL: {log.url}')

Common Transaction Webhook Issues

IssueSymptomsSolution
Duplicate fulfillmentSame order processed multiple timesImplement idempotency using event_id
Missing eventsSome transactions not triggering webhooksCheck trigger configuration, webhook logs
Timeout errorsWebhooks failing with timeoutOptimize handler, defer heavy processing

For comprehensive debugging, see:


Testing Transaction Webhooks

Test transaction webhooks before production deployment:

import time
 
import payload
 
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Create a test payment to trigger webhook
transaction = pl.Transaction.create(
    {
        'type': 'payment',
        'amount': 10.00,
        'description': 'Test payment to trigger webhook',
        'receiver': {'account_id': 'acct_processing123'},
        'sender': {'method_id': 'pm_test456'},
    }
)
 
print(f'Payment created: {transaction.id}')
print(f'Status: {transaction.status["value"]}')
print('Webhook should be triggered when payment is processed')
 
# Wait a few seconds then check webhook logs
time.sleep(5)
 
webhook = pl.Webhook.select('*', 'logs').get('wh_webhook123')
recent_log = webhook.logs[0]
 
print(f'\nMost recent webhook trigger:')
print(f'Date: {recent_log.created_at}')
print(f'HTTP Status: {recent_log.http_status}')
print(f'Transaction: {recent_log.triggered_on["id"]}')

Testing Checklist

Verify these scenarios with test transactions:

  • Triggers successfully received
  • Duplicate webhooks don't cause duplicate fulfillment (idempotency)
  • Handler processes webhooks within 30 seconds
  • Signature verification rejects invalid signatures
  • Failed external API calls don't block webhook acknowledgment

For complete testing strategies, see:


Transaction Webhook Best Practices

Follow these transaction-specific best practices for reliable payment automation.

Idempotency for Transaction Events

# Track processed webhook events to prevent duplicate fulfillment
from flask import Flask, request
 
app = Flask(__name__)
 
processed_events = set()
 
@app.route("/webhooks/transaction-processed", methods=["POST"])
def handle_processed():
    event = request.json
    event_id = event["event_id"]
    triggered_on = event["triggered_on"]
 
    if event_id in processed_events:
        print(f"Event {event_id} already processed")
        return "OK", 200
 
    processed_events.add(event_id)
 
    transaction = pl.Transaction.get(triggered_on["id"])
    fulfill_order(transaction)
 
    return "OK", 200

Use event_id for idempotency checks, not transaction ID. A single transaction can trigger multiple webhook events (e.g., authorized then processed), so transaction IDs aren't unique across events.

Handle All Transaction States

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/webhooks/transaction-events", methods=["POST"])
def handle_transaction_event():
    event = request.json
    trigger = event["trigger"]
    triggered_on = event["triggered_on"]
 
    handlers = {
        "processed": handle_processed,
        "decline": handle_decline,
        "refund": handle_refund,
        "reject": handle_reject,
    }
 
    handler = handlers.get(trigger)
    if handler:
        handler(triggered_on["id"])
    else:
        print(f"Unhandled trigger: {trigger}")
 
    return "OK", 200

Monitor Transaction Webhook Health

Track key metrics for transaction webhooks:

  • Average time to process webhook events
  • Rate of duplicate webhook processing attempts
  • Failure rate for each transaction trigger type

Schema Reference


Transaction webhook configuration:

Webhook Configuration for Transactions

trigger
enum[string]
No description
Values: bank_account_reject, reject, refund, void, chargeback, chargeback_reversal, automatic_payment, payment, processed, authorized, adjusted, decline, processing_status, deposit, payment_activation:status, payment_link:status, reversal, credit, transaction:operation, transaction:operation:clear, payout, withdraw
url
string
Triggered URL
Max length: 512
retries
integer (int64)
Number of times to retry a webhook on failure

For complete transaction field reference:

Next Steps

Enhance webhook functionality and transaction automation


Webhook Infrastructure

Master Webhooks Overview for webhook concepts and setup, implement Signature Verification to secure webhook endpoints with HMAC-SHA256, and use Debugging Webhooks to monitor and troubleshoot webhook delivery.

Transaction Processing

Handle Voids and Refunds for refund and void webhooks, process Payment Declines to respond to declined transactions, and understand Transaction Status to track the complete payment lifecycle.

Payment Workflows

Build Recurring Payments to handle automatic payment webhooks, implement Two-Step Processing for authorize and capture workflows, and manage Payment Methods for payment method webhooks.


Related articles