Local development and testing

Updated

Test against Paddle’s sandbox with EDD Test Mode on. Paddle’s sandbox test card is 4242 4242 4242 4242 with any future expiry date and any CVC.

Webhooks on a local site

Paddle cannot reach *.local hosts, and Paddle Classic refuses to open a checkout whose webhook URL is unreachable. Expose the site with a tunnel:

cloudflared tunnel --url http://site.local --http-host-header site.local

Then point webhooks at it with a must-use plugin:

wp-content/mu-plugins/paddle-tunnel.php

<?php
add_filter( 'wpinterface_paddle_webhook_base_url', function () {
    return 'https://<random>.trycloudflare.com/';
} );

Quick-tunnel URLs change on every start

Update the filter, the Classic alert URL, or click Set up webhook again (Billing).

Logging

Turn on Debug logging and watch Downloads → Tools → Debug Log (lines prefixed Paddle:). Problems are logged even with the setting off.

Simulating Billing webhooks

Sign the exact raw body with the sandbox webhook secret:

ts=$(date +%s)
body='{"event_id":"evt_test","event_type":"transaction.completed","data":{ … }}'
sig=$(printf '%s:%s' "$ts" "$body" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -X POST https://site/wp-json/wpinterface-paddle/v1/webhook \
     -H "Paddle-Signature: ts=$ts;h1=$sig" \
     -H 'Content-Type: application/json' -d "$body"

Mocking the Paddle API

In PHP tests, short-circuit HTTP with pre_http_request for sandbox-api.paddle.com / sandbox-vendors.paddle.com, and inject fake credentials:

tests/bootstrap.php

add_filter( 'edd_get_option_paddle_billing_sandbox_api_key', function () {
    return 'pdl_sdbx_apikey_test';
} );

Classic webhooks cannot be signed locally without Paddle’s private key. Unit-test Signature::verify_classic() with your own key pair, and call Classic_Driver::handle_alert( $fields, true ) directly for alert logic.

Was this page helpful?