Skip to main content

Webhooks in WordPress

The Coinsnap WordPress plugin registers a webhook endpoint that Coinsnap calls when a payment event occurs. This is how the plugin reliably updates WooCommerce order statuses.


Webhook endpoint

The plugin hooks into the WooCommerce API endpoint system:

add_action('woocommerce_api_coinsnap', [ $this, 'processWebhook' ]);

This means the webhook URL is:

https://yoursite.com/?wc-api=coinsnap

On sites with pretty permalinks enabled, WooCommerce also routes this to https://yoursite.com/wc-api/coinsnap/. The plugin registers this URL with Coinsnap automatically when you save settings.


Registering the webhook with Coinsnap

The plugin automatically registers the webhook URL with Coinsnap when you save the plugin settings. You can verify it by going to Coinsnap Dashboard → Webhooks.

If automatic registration fails, add the webhook manually:

  1. Go to Coinsnap Dashboard → Webhooks → Add Webhook
  2. Enter the webhook URL (see above)
  3. Select events: Settled, Expired
  4. Copy the Signing Secret
  5. The Signing Secret is stored automatically by the plugin in the coinsnap_webhook option (coinsnap_webhook['secret']). If you registered the webhook manually, paste the secret into the plugin settings under WooCommerce → Settings → Coinsnap → Webhook Secret.

Signature verification

The plugin verifies the X-Coinsnap-Sig header before processing any webhook. From AbstractGateway.php:

// Get signature from header
$headers = getallheaders();
$signature = null;
foreach ($headers as $key => $value) {
if (strtolower($key) === 'x-coinsnap-sig') {
$signature = $value;
}
}

if (null === $signature) {
wp_die('Authentication required', '', [ 'response' => 401 ]);
}

// Validate — delegates to Coinsnap\Client\Webhook::isIncomingWebhookRequestValid()
if (! $this->apiHelper->validWebhookRequest($signature, $rawPostData)) {
wp_die('Invalid authentication signature', '', [ 'response' => 401 ]);
}

The webhook secret is read from get_option('coinsnap_webhook')['secret'].


Order status mapping

Default mappings from OrderStates.php (configurable in WooCommerce → Settings → Coinsnap → Order States):

Webhook eventDefault WooCommerce statusMeaning
NewpendingPayment attempt started
Processingon-holdFull payment received, waiting for on-chain settlement
SettledcompletedPayment settled — trigger fulfillment
ExpiredcancelledInvoice expired without payment

The Settled mapping defaults to completed rather than processing. WooCommerce automatically sets virtual/downloadable orders to completed regardless; for physical products you may want to change this to processing so orders enter your fulfillment queue.

All mappings are user-configurable from the settings page.


Debugging webhooks

If orders are not updating after payment:

  1. Check the Coinsnap dashboard — go to Webhooks → [your webhook] → Deliveries and look for failed deliveries
  2. Check the webhook URL — confirm the registered URL is reachable from the internet (not localhost)
  3. Check WordPress debug logs — enable WP_DEBUG_LOG in wp-config.php:
    define('WP_DEBUG', true);
    define('WP_DEBUG_LOG', true);
  4. Redeliver manually — use the Redeliver button in the Coinsnap dashboard to retry a failed delivery

Example webhook payload

{
"type": "Settled",
"invoiceId": "inv_4Kz9mXpQ2rNvBtYwLs8cDf",
"metadata": {
"orderId": "142"
},
"additionalStatus": "None"
}

The metadata.orderId field contains the WooCommerce order ID as passed when creating the invoice. The plugin uses this to look up the WooCommerce order and update its status.