Skip to content

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. Install and register the provider
  2. Configure your credentials and webhook URL
  3. Enable the provider on a region in Medusa Admin
  4. Wire up your storefront

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.

RequirementDetails
Medusav2.8+ (peer dependency)
Node20+
Cryptonly accountmerchant.cryptonly.net
Public HTTPS backendCryptonly must reach {MEDUSA_BACKEND_URL}/hooks/payment/cryptonly_cryptonly

Install the plugin:

Terminal window
npm install @cryptonly/medusa-plugin-cryptonly

Register the provider in medusa-config.ts:

module.exports = defineConfig({
modules: [
{
resolve: "@medusajs/medusa/payment",
options: {
providers: [
{
resolve: "@cryptonly/medusa-plugin-cryptonly/providers/cryptonly",
id: "cryptonly",
options: {
apiKey: process.env.CRYPTONLY_API_KEY,
webhookSigningKey: process.env.CRYPTONLY_WEBHOOK_SIGNING_KEY,
sandbox: process.env.CRYPTONLY_SANDBOX === "true",
backendUrl: process.env.MEDUSA_BACKEND_URL,
},
},
],
},
},
],
})

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

OptionRequiredDescription
apiKeyYesAPI key from the account Security settings. The key selects the account. See required scopes.
webhookSigningKeyYesSigning key from the same account. Verifies incoming Cryptonly webhooks
backendUrlYesPublic HTTPS Medusa origin used to build the webhook URL
webhookUrlYesFull HTTPS webhook URL (overrides backendUrl)
sandboxNoUse the Cryptonly sandbox API (default: false)
returnUrlNoDefault browser URL after cancelled / expired
successUrlNoDefault browser URL after paid / overpaid
failedUrlNoDefault browser URL after failed / suspended / partially_paid
expiresInMinutesNoInvoice expiry passed to Cryptonly (default: 1440)

Provide either backendUrl or webhookUrl.

Create the key on the account’s Security settings and enable only these scopes:

ScopeUsed for
currencies.listConfirm the API key can reach the merchant API
invoices.createStart checkout (POST /invoice) and recreate an invoice when the cart amount or currency changes
invoices.listRead the invoice while authorizing, polling status, or retrieving the session (GET /invoice)
invoices.cancelCancel the invoice when the customer abandons checkout or the cart total changes (POST /invoice/cancel)

Keep in mind:

  • apiKey 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.

Cryptonly’s hosted page shows a “Back to Store” button whose destination depends on the invoice status:

OptionUsed when invoice status is
successUrlpaid / overpaid
returnUrlcancelled / expired
failedUrlfailed / suspended / partially_paid

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.

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.)

const base = baseUrl.replace(/\/$/, "")
const successUrl = `${base}/order/cryptonly/return?cart_id=${cart.id}`
const returnUrl = `${base}/checkout?step=review&cart_id=${cart.id}`
const failedUrl = `${base}/checkout?step=review&cart_id=${cart.id}`
const { payment_collection } = await initiatePaymentSession(cart, {
provider_id: "pp_cryptonly_cryptonly",
data: { returnUrl, successUrl, failedUrl },
})
const session = payment_collection.payment_sessions.find(
(s) => s.provider_id.startsWith("pp_cryptonly_")
)
window.location.href = session.data.paymentPageUrl

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:

import Cryptonly from "@modules/common/icons/cryptonly"
pp_cryptonly_cryptonly: {
title: "Cryptonly (crypto)",
icon: <Cryptonly />,
},

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

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

Cryptonly statusMedusa result
paid, overpaidCaptured (successful)
created, processingPending (no completion)
partially_paidFailed
failed, expired, suspendedFailed
cancelledCanceled

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