For the complete documentation index, see llms.txt. This page is also available as Markdown.

Medusa

Accept cryptocurrency payments in Medusa v2 through Cryptonly hosted checkout.

The Cryptonly payment provider for Medusa lets your Medusa v2 store accept cryptocurrency payments through Cryptonly's hosted checkout.

Customers select Cryptonly at checkout, pay on the Cryptonly payment page, and return to your store. The Medusa payment status updates automatically via signed webhooks — no manual reconciliation.

Setting up takes four steps:

  1. Enable the provider on a region in Medusa Admin

  2. Wire up your storefront

How the payment flow works

At a high level:

  1. The customer checks out and chooses Cryptonly as the payment method.

  2. Medusa creates a Cryptonly invoice server-side — your API key never reaches the browser.

  3. The storefront redirects the customer to the hosted payment page (paymentPageUrl).

  4. After payment, Cryptonly sends signed webhooks to your Medusa backend.

  5. The provider verifies each webhook and marks the payment session captured once the invoice is paid or overpaid.

For details on invoices and statuses, see Invoices.

Requirements

Requirement
Details

Medusa

v2.8+ (peer dependency)

Node

20+

Cryptonly account

merchant.cryptonly.net

Public HTTPS backend

Cryptonly must reach {MEDUSA_BACKEND_URL}/hooks/payment/cryptonly_cryptonly

Installation

Install the plugin:

Register the provider in medusa-config.ts:

Finally, enable the provider on a region in Medusa Admin.

Configuration

Option
Required
Description

apiKey

Yes

Tenant API key (Settings → Security → API keys)

accountId

Yes

Cryptonly account UUID

webhookSigningKey

Yes

Verifies incoming Cryptonly webhooks

backendUrl

Yes

Public HTTPS Medusa origin used to build the webhook URL

webhookUrl

Yes

Full HTTPS webhook URL (overrides backendUrl)

sandbox

No

Use the Cryptonly sandbox API (default: false)

returnUrl

No

Default browser URL after cancelled / expired

successUrl

No

Default browser URL after paid / overpaid

failedUrl

No

Default browser URL after failed / suspended / partially_paid

expiresInMinutes

No

Invoice expiry passed to Cryptonly (default: 1440)

Provide either backendUrl or webhookUrl.

Keep in mind:

  • apiKey, accountId, and webhookSigningKey must be non-empty — otherwise the provider will not register.

  • Webhook URLs must be public HTTPS. Localhost and private IPs are rejected.

  • The provider sets webhookUrl automatically on every invoice it creates, so you do not need to configure a default invoice webhook in the Cryptonly dashboard.

Browser return URLs

Cryptonly's hosted page shows a "Back to Store" button whose destination depends on the invoice status:

Option
Used when invoice status is

successUrl

paid / overpaid

returnUrl

cancelled / expired

failedUrl

failed / suspended / partially_paid

These are browser links, not server webhooks. Order completion always depends on Cryptonly calling your Medusa webhookUrl — even if the customer never clicks "Back to Store". See Invoice statuses for the full status model.

A few things to know:

  • Any http or https URL is accepted, including local storefronts such as http://localhost:8000/....

  • Provider options are only fallbacks. Prefer passing per-session values in the initiatePaymentSession data so each cart gets the right confirmation or retry URL.

Recommended storefront mapping:

  • successUrl → confirmation / poll page (e.g. /order/cryptonly/return?cart_id=…)

  • returnUrl / failedUrl → checkout review, so the customer can try again (e.g. /checkout?step=review&cart_id=…)

Include cart_id on all three URLs. On the Next.js starter, set the _medusa_cart_id cookie to SameSite=Lax so it is sent on the top-level return from Cryptonly — otherwise checkout may 404 after a cancel.

For webhook signing details, see Webhooks. For sandbox testing (simulated payments, test webhooks), see Testing your integration.

Storefront

Start a new session on every pay attempt

On each "Pay with Cryptonly" click, call initiatePaymentSession again and redirect to the new paymentPageUrl. Do not reuse a URL from an earlier session — once the customer cancels or the invoice expires on Cryptonly, that URL is dead. (Same principle as Stripe Checkout: one session per pay attempt.)

Checkout label and icon

Checkout labels and icons are storefront-owned — Medusa does not expose them from the provider. On the Next.js starter, add an entry to paymentInfoMap in src/lib/constants.tsx:

A reference icon component ships with the plugin under assets/storefront/cryptonly.tsx, and PNG/SVG brand marks are available in assets/ as well.

Status mapping

The plugin listens for invoice.statusChanged webhooks and maps Cryptonly statuses to Medusa results as follows:

Cryptonly status
Medusa result

paid, overpaid

Captured (successful)

created, processing

Pending (no completion)

partially_paid

Failed

failed, expired, suspended

Failed

cancelled

Canceled

Live demo (sandbox)

Try the full flow - no real funds needed: medusa-demo.cryptonly.net

See also

Last updated