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:
Enable the provider on a region in Medusa Admin
How the payment flow works
At a high level:
The customer checks out and chooses Cryptonly as the payment method.
Medusa creates a Cryptonly invoice server-side — your API key never reaches the browser.
The storefront redirects the customer to the hosted payment page (
paymentPageUrl).After payment, Cryptonly sends signed webhooks to your Medusa backend.
The provider verifies each webhook and marks the payment session captured once the invoice is
paidoroverpaid.
For details on invoices and statuses, see Invoices.
Requirements
Medusa
v2.8+ (peer dependency)
Node
20+
Cryptonly account
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
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, andwebhookSigningKeymust be non-empty — otherwise the provider will not register.Webhook URLs must be public HTTPS. Localhost and private IPs are rejected.
The provider sets
webhookUrlautomatically 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:
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
httporhttpsURL is accepted, including local storefronts such ashttp://localhost:8000/....Provider options are only fallbacks. Prefer passing per-session values in the
initiatePaymentSessiondataso 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:
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