CoinPay Payment Gateway for WooCommerce
Accept cryptocurrency payments through CoinPay in your WooCommerce store.
by CoinPay · github.com/coinpay-finance/wordpress-integration-plugin · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/coinpay-finance/wordpress-integration-plugin/archive/refs/heads/main.zipAccept cryptocurrency payments through CoinPay in your WooCommerce store.
This plugin talks to the CoinPay merchant payment API (https://platform.coinpay.finance/api/v1/coin-pay) — the same first-party contract implemented by CoinPay's official PHP, Laravel, and Go SDKs. It creates a payment, redirects the customer to CoinPay's hosted checkout, and relies on a signed server-to-server webhook (not the browser redirect) to mark the WooCommerce order as paid once the blockchain transaction actually confirms.
Requirements
- WordPress 5.8+
- WooCommerce 3.0+
- PHP 7.4+ with the
curlandjsonextensions (via WordPress's HTTP API) - A CoinPay merchant account and API key
- A store whose active WooCommerce currency is USD (see Currency below)
Installation
- Copy this plugin's folder into
wp-content/plugins/(or zip it and upload via Plugins → Add New → Upload Plugin). - Activate CoinPay Payment Gateway for WooCommerce from the WordPress admin Plugins screen.
- Go to WooCommerce → Settings → Payments → CoinPay to configure it.
Configuration
API key
Paste your CoinPay merchant API key into the API Key field. It's sent as-is in the Authorization header of every API request (no Bearer prefix — CoinPay's API expects the raw key). If you paste a key that still has a Bearer prefix from somewhere else, the plugin strips it automatically as a defensive measure, but you shouldn't need to include it.
Staging mode
Check Staging Mode to point the plugin at CoinPay's staging environment (https://staging.coinpay.finance/api/v1/coin-pay) instead of production, for testing. A warning banner appears in the WordPress admin bar while staging mode is on so it's hard to leave on by accident.
Webhook setup
Crypto payments confirm asynchronously — unlike a card payment, there's no single instant "approved/declined" response. Because of that, this plugin never marks an order paid from the customer's browser redirect. Only a webhook from CoinPay does that.
- On the CoinPay settings screen, copy the webhook URL shown under "Webhook Settings" — it looks like:
https://your-site.example/wp-json/coinpay/v1/webhook - In your CoinPay merchant dashboard, register that URL as the webhook endpoint for this store.
- Copy the signing secret CoinPay shows you for that endpoint into the plugin's Webhook Secret field.
The webhook endpoint is a WordPress REST API route (not the older woocommerce_api_* query-var style) specifically because a REST request gives clean access to the raw request body, which is required to verify the HMAC signature — the signature is computed over the raw bytes, not a re-encoded copy of the payload.
Two verification schemes are supported, matching what CoinPay's backend actually sends today:
- Primary — HMAC-SHA256: the request carries
X-Coinpay-Signature: v1=<hex-hmac-sha256>,X-Coinpay-Timestamp, andX-Coinpay-Deliveryheaders. The plugin recomputes the signature over"{timestamp}.{deliveryId}.{rawBody}"using your webhook secret, compares it with a constant-time comparison, and rejects anything whose timestamp is more than 300 seconds old or in the future (replay protection). - Legacy (deprecated, still accepted): a static
SECRETheader compared directly (constant-time) against your configured webhook secret. CoinPay still sends this for senders that haven't moved to signature verification; new integrations should rely on the HMAC scheme.
The signature-verification logic lives in includes/class-coinpay-webhook-verifier.php and has no WordPress dependency, so it's covered by a PHPUnit test suite in tests/ (see Testing) independent of a real WordPress install.
How the payment flow works
- The customer chooses CoinPay at checkout and places the order. WooCommerce redirects them to this gateway's "pay for order" page.
- The plugin calls CoinPay's
POST /paymentendpoint with the order amount (in USD), aredirect_urlpointing back to WooCommerce's own order-received ("thank you") page, and aclient_ref_idderived from the order ID and order key. CoinPay returns a ready-to-use hosted checkouturland atransaction_id, which the plugin stores on the order and redirects the customer to directly — there's no separate "authorize, then build a start-pay URL" step. - The customer completes (or abandons) payment on CoinPay's hosted page and is redirected back to WooCommerce's order-received page. That page reflects the order's real, database-backed status — it never trusts anything in the redirect's query string — so if the payment hasn't confirmed yet, the customer sees a "your payment is awaiting confirmation" message instead of a false "paid" state.
- Once the blockchain transaction actually confirms, CoinPay calls your configured webhook. The plugin verifies the signature, looks up the order by the
transaction_idit stored in step 2, and calls$order->payment_complete()— this is the only place in the plugin that ever marks an order as paid.
An order admin can also click Re-check Transaction Status on the order screen to manually call CoinPay's status-check endpoint and reconcile the order if a webhook was somehow missed.
Currency
CoinPay's create payment API takes a plain numeric amount in US dollars — there's no currency code parameter and no built-in FX conversion. This plugin does not perform currency conversion of its own. It only sends wc_get_order()->get_total() as-is, which is only correct if your store's active WooCommerce currency is already USD.
If your store currency is anything else, the plugin:
- shows a persistent admin notice on WooCommerce → Settings → Payments explaining the mismatch, and
- refuses to send the customer to CoinPay at checkout (with a clear error) rather than silently charging the wrong amount.
If you need to sell in another currency, set your WooCommerce store currency to USD, or convert the price yourself before checkout — this plugin intentionally does not guess at an exchange rate.
Refunds are not supported
There is currently no known CoinPay refund API. None of CoinPay's first-party SDKs (PHP, Laravel, Go) implement one either. Rather than inventing an endpoint that may not exist, this plugin does not declare refunds support and has no refund code path — if you need to refund a customer, do it manually outside WooCommerce (directly with CoinPay/on-chain) and update the order's status/notes accordingly.
Translations
All customer- and admin-facing strings are in English by default and wrapped for translation under the wc-coinpay text domain. A Persian (fa_IR) translation is provided in languages/wc-coinpay-fa_IR.po, along with the languages/wc-coinpay.pot template for adding further languages.
Note: WordPress loads compiled .mo files, not .po source files. Before shipping a release, compile the .po into a .mo (e.g. msgfmt languages/wc-coinpay-fa_IR.po -o languages/wc-coinpay-fa_IR.mo, or wp i18n make-mo languages/ via WP-CLI) — that step wasn't run here since neither msgfmt nor WP-CLI is available in this workspace.
Testing
The webhook-signature verifier (CoinPay_Webhook_Verifier) is the one piece of this plugin with no WordPress dependency, so it has an isolated PHPUnit suite:
composer install
composer test
# or: php vendor/bin/phpunit
The rest of the plugin is tightly coupled to WordPress/WooCommerce runtime functions (wc_get_order, wp_remote_post, the Settings API, REST API, etc.) and is verified with php -l plus manual review rather than automated tests, since there's no practical way to exercise it without a real WordPress install.
What changed from earlier versions
Earlier code in this repository actually implemented ZarinPal's API (an unrelated Iranian card-payment processor) behind CoinPay-branded labels — a display-only rename that was never backed by a real CoinPay integration. This includes the ZarinPal merchant_id/"authority"/StartPay redirect flow, Iranian Rial currency-multiplier logic (IRT/IRHR/IRHT), and ZarinPal's GraphQL-based refund API. None of that shipped to a real merchant (there were no releases/tags), so it was replaced outright with a genuine CoinPay integration as described above, with no backward-compatibility constraints beyond keeping the WordPress-facing gateway ID (WC_COINPAY) stable.
License
GPLv3 or later.