Webhooks
WalletPro sends signed outbound webhook events to your endpoint whenever wallet-related state changes occur, enabling real-time integration with external systems such as ERPs, loyalty platforms, fraud monitors, and custom dashboards.
Overview
Webhooks are HTTP POST requests WalletPro sends to a URL you configure whenever a notable wallet event occurs. Each request carries a JSON payload describing the event and is signed with an HMAC-SHA256 signature so you can verify its authenticity before processing it.
WalletPro webhooks are independent of the native WooCommerce webhook system. They are registered and managed under WooCommerce > WalletPro > Webhooks and support per-endpoint event filtering, retry logic, and delivery logs.
Supported Events
| Event key | Fires when |
|---|---|
wallet.credited |
Balance is added to a wallet (top-up, cashback, gift credit, admin manual credit, referral reward) |
wallet.debited |
Balance is deducted from a wallet (order payment, admin manual debit, withdrawal) |
wallet.topup.completed |
A customer-initiated top-up reaches the completed status |
wallet.topup.failed |
A top-up payment fails or is declined |
wallet.reload.triggered |
An auto-reload rule fires because balance fell below threshold |
wallet.transfer.sent |
A customer sends a wallet transfer to another customer |
wallet.transfer.received |
A customer receives a wallet transfer |
wallet.withdrawal.requested |
A customer submits a withdrawal request |
wallet.withdrawal.approved |
An admin approves a pending withdrawal |
wallet.withdrawal.rejected |
An admin rejects a pending withdrawal |
wallet.redemption_code.redeemed |
A customer successfully redeems a code |
wallet.milestone.awarded |
A milestone reward is credited to a wallet |
wallet.cashback.awarded |
A cashback reward is credited following a qualifying order |
wallet.referral.rewarded |
A referral reward is issued to the referrer or referee |
wallet.spending_limit.blocked |
A transaction is blocked because it would exceed a spending limit |
wallet.kyc.required |
A transaction is gated pending KYC completion |
How It Works
When a triggering event occurs, WalletPro:
- Builds a JSON payload for the event.
- Computes an HMAC-SHA256 signature over the raw payload body using the endpoint's secret key.
- Sends an HTTP
POSTto your endpoint URL with the signature in theX-WalletPro-Signatureheader. - Considers the delivery successful if your endpoint responds with any
2xxstatus code within 10 seconds. - Retries failed deliveries up to five times with exponential back-off (30 s, 2 min, 10 min, 1 h, 4 h). After five failures the endpoint is marked suspended and no further deliveries are attempted until you re-enable it.
Your endpoint must return a 2xx response quickly. Do your heavy processing asynchronously, add the payload to a queue and return 200 OK immediately. A slow response that times out is treated as a failure and triggers a retry.
Configuring Webhook Endpoints
Adding an Endpoint
- Go to WooCommerce > WalletPro > Webhooks.
- Click Add Endpoint.
- Fill in the fields:
Field Description Endpoint Name A label for internal reference (e.g., "ERP sync", "Fraud monitor"). Endpoint URL The HTTPS URL WalletPro will POST events to. Secret Key Used to sign payloads. Auto-generated; you can replace it with your own value. Store this securely, it is shown only once. Events Check the events this endpoint should receive. Leave all unchecked to receive every event. Status Active or Disabled. Disabled endpoints queue no deliveries. API Version Payload schema version. Currently v1. - Click Save Endpoint. WalletPro immediately sends a
pingevent to verify the URL is reachable.
Endpoint URLs must use HTTPS. HTTP URLs are rejected at save time. During local development use a tunneling tool such as ngrok or Cloudflare Tunnel to expose a localhost port over HTTPS.
Editing and Deleting Endpoints
Click the endpoint name in the list to open it for editing. To delete an endpoint, open it and click Delete Endpoint. Deletion is permanent and clears the delivery log for that endpoint.
Rotating the Secret Key
Open the endpoint, click Rotate Secret, copy the new secret immediately, then click Save Endpoint. WalletPro starts signing with the new key immediately. Update your receiver before rotating to avoid a gap where signatures fail.
Delivery Logs
Each endpoint has a Delivery Log tab showing the last 100 delivery attempts. Each row shows:
- Delivery timestamp (UTC)
- Event key
- HTTP status code returned by your endpoint
- Response time in milliseconds
- Attempt number (1 to 5)
- A View Payload link that shows the exact JSON sent
To manually retry a failed delivery, click Retry on the log row. This sends the original payload with the same event ID.
Payload Structure
All events share a common envelope. Event-specific data appears in the data object.
// Common envelope (all events)
{
"id": "wph_01J3KXMQ8T4N9VBCDE2F5G7H", // unique delivery ID
"event": "wallet.credited",
"api_version": "v1",
"created_at": "2026-06-20T14:32:07Z",
"site_url": "https://example.com",
"data": { /* event-specific object */ }
}
Example: wallet.credited
{
"id": "wph_01J3KXMQ8T4N9VBCDE2F5G7H",
"event": "wallet.credited",
"api_version": "v1",
"created_at": "2026-06-20T14:32:07Z",
"site_url": "https://example.com",
"data": {
"transaction_id": 4821,
"user_id": 103,
"user_email": "jane@example.com",
"amount": "25.00",
"currency": "USD",
"balance_after": "75.00",
"type": "cashback",
"reason": "5% cashback on order #1204",
"order_id": 1204,
"ledger_hash": "a3f9c2e..."
}
}
Example: wallet.debited
{
"id": "wph_01J3KY7PQR2S4UVWXY8Z9ABCD",
"event": "wallet.debited",
"api_version": "v1",
"created_at": "2026-06-20T15:10:44Z",
"site_url": "https://example.com",
"data": {
"transaction_id": 4822,
"user_id": 103,
"user_email": "jane@example.com",
"amount": "40.00",
"currency": "USD",
"balance_after": "35.00",
"type": "order_payment",
"reason": "Partial payment for order #1205",
"order_id": 1205,
"ledger_hash": "b7d4e1f..."
}
}
Example: wallet.transfer.sent
{
"id": "wph_01J3KZ3MNOP5QRSTU6VWXYZ12",
"event": "wallet.transfer.sent",
"api_version": "v1",
"created_at": "2026-06-20T16:05:22Z",
"site_url": "https://example.com",
"data": {
"transfer_id": 319,
"sender_id": 103,
"sender_email": "jane@example.com",
"recipient_id": 207,
"recipient_email": "bob@example.com",
"amount": "10.00",
"currency": "USD",
"note": "Splitting lunch"
}
}
Verifying Signatures
Every request includes an X-WalletPro-Signature header of the form:
X-WalletPro-Signature: t=1750429927,v1=3d4f8a...
The t component is the Unix timestamp WalletPro used when computing the signature. The v1 component is the HMAC-SHA256 hex digest of {t}.{raw_body} using your endpoint's secret key.
Verification Steps
- Extract
tandv1from the header. - Reject the request if
tis more than 300 seconds in the past (replay protection). - Compute
HMAC-SHA256(secret, "{t}.{raw_body}"). - Compare your computed digest to
v1using a constant-time comparison. Reject if they do not match.
PHP Example
function walletpro_verify_webhook( string $secret ): bool {
$header = $_SERVER['HTTP_X_WALLETPRO_SIGNATURE'] ?? '';
$raw_body = file_get_contents( 'php://input' );
if ( ! preg_match( '/^t=(\d+),v1=([a-f0-9]+)$/', $header, $m ) ) {
return false;
}
[ , $timestamp, $received_sig ] = $m;
if ( abs( time() - (int) $timestamp ) > 300 ) {
return false; // reject stale requests
}
$expected_sig = hash_hmac( 'sha256', $timestamp . '.' . $raw_body, $secret );
return hash_equals( $expected_sig, $received_sig );
}
if ( ! walletpro_verify_webhook( 'your_secret_key' ) ) {
http_response_code( 401 );
exit;
}
$payload = json_decode( file_get_contents( 'php://input' ), true );
// handle $payload['event'] ...
Node.js Example
const crypto = require('crypto');
function verifyWalletProWebhook(secret, rawBody, signatureHeader) {
const match = signatureHeader.match(/^t=(\d+),v1=([a-f0-9]+)$/);
if (!match) return false;
const [, timestamp, receivedSig] = match;
if (Math.abs(Date.now() / 1000 - parseInt(timestamp, 10)) > 300) {
return false;
}
const expectedSig = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expectedSig),
Buffer.from(receivedSig)
);
}
// Express example
app.post('/webhooks/walletpro', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-walletpro-signature'] ?? '';
if (!verifyWalletProWebhook(process.env.WALLETPRO_SECRET, req.body, sig)) {
return res.status(401).end();
}
const payload = JSON.parse(req.body);
// dispatch on payload.event ...
res.status(200).end();
});
Always read the raw request body before parsing JSON, signature verification operates on the raw bytes. Parsing first and re-serializing will produce a different byte string and cause signature mismatches.
Idempotency
Each delivery has a unique id field (e.g., wph_01J3KXMQ8T4N9VBCDE2F5G7H). Because WalletPro retries on failure, your endpoint may receive the same event more than once. Store the id in a deduplication table and skip processing if you have already handled it.
// Pseudo-code deduplication
const deliveryId = payload.id;
if (await db.has('processed_webhooks', deliveryId)) {
return res.status(200).end(); // already handled, ack and discard
}
await db.insert('processed_webhooks', { id: deliveryId, received_at: new Date() });
// proceed with processing
Sending a Test Event
To verify your endpoint is working without waiting for a real transaction:
- Go to WooCommerce > WalletPro > Webhooks and open the endpoint.
- Click Send Test Event.
- Select an event type from the dropdown.
- Click Send. WalletPro delivers a payload with
"test": truein the envelope and synthetic data values. - Check the Delivery Log tab to see the response your endpoint returned.
You can also trigger a test delivery via WP-CLI:
# Send a test wallet.credited event to endpoint ID 3
wp wallet webhook test --endpoint=3 --event=wallet.credited
Managing Endpoints via WP-CLI
| Command | Description |
|---|---|
wp wallet webhook list |
List all registered endpoints with their status and event filters. |
wp wallet webhook get <id> |
Show full configuration for one endpoint. |
wp wallet webhook create --url=<url> --events=<list> |
Register a new endpoint. Prints the generated secret key on creation. |
wp wallet webhook update <id> --status=disabled |
Update any field of an existing endpoint. |
wp wallet webhook delete <id> |
Delete an endpoint and its delivery log. |
wp wallet webhook rotate-secret <id> |
Generate and apply a new secret key; prints the new key once. |
wp wallet webhook test --endpoint=<id> --event=<key> |
Send a test event to the specified endpoint. |
wp wallet webhook logs <id> |
Print the last 50 delivery log entries for an endpoint. |
Security Considerations
- Keep your secret key private. It is the only mechanism that establishes trust in deliveries. Do not log it, commit it to source control, or expose it in client-side code.
- Always verify signatures before acting on a payload. An unverified endpoint is open to spoofed events.
- Enforce the timestamp check. The 300-second window prevents replayed requests from being processed after an interception.
- Use HTTPS with a valid certificate. WalletPro will not deliver to endpoints with invalid or self-signed certificates in production.
- Scope your event subscriptions. Subscribe only to the events your receiver actually handles. This reduces noise and narrows the attack surface if a spoofed delivery were to slip through.
WalletPro logs the full request payload in the delivery log for debugging. If your store handles sensitive data, ensure access to WooCommerce > WalletPro > Webhooks is restricted to trusted admin users only.
Payload Field Reference
Envelope Fields (all events)
| Field | Type | Description |
|---|---|---|
id |
string | Unique delivery ID. Use for idempotency. |
event |
string | Event key (e.g., wallet.credited). |
api_version |
string | Payload schema version. Currently v1. |
created_at |
string (ISO 8601 UTC) | Timestamp when the event was created. |
site_url |
string | The WordPress site URL that sent the event. |
test |
boolean | Present and true only on test deliveries. Omitted on live events. |
data |
object | Event-specific payload. Fields vary by event type. |
Common data Fields (transaction events)
| Field | Type | Description |
|---|---|---|
transaction_id |
integer | WalletPro ledger transaction ID. |
user_id |
integer | WordPress user ID of the wallet owner. |
user_email |
string | Email address of the wallet owner at time of transaction. |
amount |
string (decimal) | Transaction amount as a decimal string (e.g., "25.00"). |
currency |
string (ISO 4217) | Currency code of the wallet (e.g., "USD"). |
balance_after |
string (decimal) | Wallet balance immediately after this transaction. |
type |
string | Transaction sub-type (e.g., cashback, order_payment, manual_credit, topup, referral). |
reason |
string | Human-readable reason recorded at transaction time. |
order_id |
integer or null | Associated WooCommerce order ID, if applicable. |
ledger_hash |
string | HMAC chain hash from the tamper-evident audit ledger for this entry. |