# WordPress and WooCommerce

> Accept crypto in a WooCommerce store or on any WordPress site with the LayerHop plugin. Customers pay with any token on any chain; orders are marked paid automatically once LayerHop verifies the payment on chain.

Source: https://docs.layerhop.com/guides/wordpress

The LayerHop plugin adds a **"Crypto (any token)" payment method to WooCommerce** and a **`[layerhop_button]` shortcode** for any WordPress site. Customers pay with any token on any chain they hold; you receive USDC or USDT on the chain you choose, straight to your wallet. Orders are marked paid automatically once LayerHop has verified the payment on chain.

The current version is **1.1.0**. The plugin is open source (GPL-2.0-or-later) at [github.com/layerhop/layerhop-wordpress](https://github.com/layerhop/layerhop-wordpress), with its [changelog](https://github.com/layerhop/layerhop-wordpress/blob/main/CHANGELOG.md).

## Install

1. Download **`layerhop.zip`** from the [latest GitHub release](https://github.com/layerhop/layerhop-wordpress/releases/latest/download/layerhop.zip) (or the mirror at [layerhop.com/downloads/layerhop-wordpress.zip](https://layerhop.com/downloads/layerhop-wordpress.zip)). Use the release asset, not GitHub's "Source code" archive: WordPress expects the `layerhop` folder at the root of the zip.
2. In WordPress: **Plugins → Add New Plugin → Upload Plugin**, choose the zip, then **Activate**.

Or with WP-CLI:

```bash
wp plugin install https://github.com/layerhop/layerhop-wordpress/releases/latest/download/layerhop.zip --activate
```

To update, upload the newer zip the same way and confirm **Replace current with uploaded**. Settings and orders are kept.

Requirements: WordPress 6.2+, PHP 7.4+, and WooCommerce 7.6+ for the payment gateway. Both the classic and the block-based checkout are supported, and the plugin is compatible with HPOS. It is translatable (text domain `layerhop`, template in `languages/layerhop.pot`).

## Set up the WooCommerce payment method

1. In the [LayerHop dashboard](https://dashboard.layerhop.com), create an API key (**API keys**). Use an `lh_test_` key first to try everything without real funds ([Test mode](/guides/test-mode)).
2. In WordPress: **WooCommerce → Settings → Payments → Crypto (any token) — LayerHop → Manage**:
   - paste the **API key**;
   - choose what you are **paid in** (for example USDC on Base) and your **payout address** on that chain;
   - choose the **payment display**: redirect to the LayerHop payment page, or a popup on your site ([embedded checkout](/guides/embedded-checkout)).
3. Copy the **webhook URL** shown on that page (`https://your-site/wp-json/layerhop/v1/webhook`). In the LayerHop dashboard, open **Webhooks**, add an endpoint with that URL (in test mode for a test key), and copy its signing secret (`whsec_…`) back into the plugin's **Webhook signing secret** field.
4. Save, then press **Test connection** in the **Setup status** box at the top of the page. It should say *Connected* with the mode of your key. The payment method appears at checkout.

The **setup checklist** in that box shows whether the method is enabled, the API key and its mode, the payout address, the webhook secret, the last webhook received, the last rejected webhook (with the reason) and the store currency.

The store currency must be **USD** (or a custom `USDC` / `USDT` currency): the order total is charged as the same amount of USDC or USDT. Other currencies hide the payment method and show an admin notice.

## How an order is paid

1. The customer picks "Crypto (any token)" at checkout and places the order. The order is **Pending payment**.
2. The plugin creates a LayerHop checkout session for the order total (reference = order number) and sends the customer to the payment, or opens it as a popup.
3. The customer pays with any token on any chain from their own wallet.
4. LayerHop verifies the delivery to your payout address and sends `checkout.session.completed` to your site. The plugin checks the signature, fetches the session again with your key to confirm it is paid for the right amount and address, then marks the order **paid** (Processing, or Completed for virtual products) with a note: who paid, with what and the transaction hash.
5. If a payment attempt fails or is refunded, the order gets a note and stays pending so the customer can try again; if the session expires unpaid (or you expire it), the order is cancelled.

While the customer waits on the thank-you page, the plugin also asks LayerHop directly every few seconds, so an order is confirmed even if a webhook is delayed or missed. Both paths run the same checks, and an order is only ever marked paid once.

## The LayerHop payment box on orders

Each LayerHop order has a **LayerHop payment** box in the order sidebar:

- the checkout session id and its live status, fetched from the LayerHop API with your key;
- the amount and settlement token, and the hosted payment link while the order is unpaid;
- what the customer paid with, the payer, and the source and delivery transactions (with explorer links in live mode);
- the amount refunded, if any;
- **Check payment status now**: asks LayerHop for the session and completes the order if it is verifiably paid, with exactly the same checks as the webhook.

## Refunds

LayerHop is non-custodial, so refunds are signed from your own wallet in the [dashboard](https://dashboard.layerhop.com), not sent by the plugin ([Refunds](/guides/refunds)). Paid orders have a **Refund in LayerHop dashboard** link, and the plugin does not offer WooCommerce's *Refund via gateway*. When a refund completes, LayerHop sends `checkout.session.refunded`:

- **full refund**: an order note with the amount and transaction, a WooCommerce refund is recorded (no restocking) and the order moves to **Refunded**;
- **partial refund**: an order note with the amount refunded so far.

Make sure your webhook endpoint receives `checkout.session.refunded` (selecting all `checkout.session.*` events covers it).

## Buttons on any WordPress site

No WooCommerce needed: create a [product](/guides/payment-buttons) in the LayerHop dashboard and add the shortcode to any page or post:

```text
[layerhop_button product="prod_…" label="Pay with crypto"]
```

The button opens the payment in a popup. In the block editor you can also add the **LayerHop payment button** block instead of typing the shortcode. Payments arrive as webhooks to your LayerHop account like any other checkout.

## Test locally

Your local WordPress cannot receive webhooks from the internet. Forward them with the [CLI](/cli):

```bash
LAYERHOP_API_KEY=lh_test_… LAYERHOP_WEBHOOK_SECRET=whsec_… \
  npx @layerhop/cli listen --forward-to http://localhost:8080/wp-json/layerhop/v1/webhook
```

Use the same `whsec_…` secret in the plugin settings. Then place a test order and press **Simulate test payment** on the payment page; the order turns paid about 10 to 20 seconds later.

## Troubleshooting

Start with the setup checklist on the settings page: it shows most problems directly.

| Problem | Fix |
|---|---|
| Payment method not shown at checkout | The method must be enabled, the store currency USD (or USDC / USDT), and the API key and payout address set. |
| **Test connection** says the key was rejected | Check the key was copied completely, isn't revoked, and is the kind you meant (test or live). |
| Orders stay pending after paying | Press **Check payment status now** on the order. Then check that the webhook endpoint exists in the dashboard for the right mode (test or live) and that the signing secret matches. The dashboard's **Webhooks** page shows each delivery and your site's response. |
| *Last rejected webhook* in the checklist, or "Invalid signature" in the log | The secret in the plugin doesn't match the endpoint's (test and live endpoints have different secrets), or the server clock is off by more than 5 minutes. If you rotated it, paste the new one; both work during the rotation's grace period. |
| An order went *On hold* | The session finished but didn't match the order (amount, recipient, token). Read the order note and check your wallet before fulfilling. |

Found a bug or want a feature? Open an issue on [GitHub](https://github.com/layerhop/layerhop-wordpress/issues). Security issues go to **security@layerhop.com**, not to a public issue.
