Cost+Docs

Medusa.js

Integrate Cost+ with your Medusa.js v2 store using the official costplus-medusa plugin

Medusa.js

Integrate Cost+ as a payment provider in your Medusa.js v2 store. The official costplus-medusa plugin registers a separate Medusa payment provider for each Cost+ payment method, uses the Hosted Payment Page flow, and verifies every status change against the Cost+ API — fully PCI DSS compliant.

Prerequisites

  • Active Cost+ merchant account
  • Medusa.js 2.14.2 or later
  • Node.js 22
  • PostgreSQL access through Medusa's DATABASE_URL
  • A public HTTPS Medusa backend URL (required for webhooks)
  • A storefront checkout that passes cart_id when initiating the payment session

Supported Payment Methods

Checkout LabelMedusa Provider IdCost+ Identifier
Credit / Debit Cardpp_costplus-credit-card_costpluscredit-card
Apple Paypp_costplus-apple-pay_costplusapple-pay
Google Paypp_costplus-google-pay_costplusgoogle-pay
Vipps MobilePaypp_costplus-vipps-mobilepay_costplusvipps-mobilepay

Each provider creates a Cost+ order for exactly one payment method, so the storefront checkout options stay aligned with the Cost+ hosted payment redirect flow.

1. Install the Plugin

Install the package in your Medusa backend project:

npm install costplus-medusa

You can also install from a tagged Git release: npm install git+ssh://git@github.com:NoPayn/costplus-medusa.git#vX.Y.Z

2. Register the Plugin and Provider

Add the plugin and payment provider to your medusa-config.ts:

import { defineConfig } from "@medusajs/framework/utils"

module.exports = defineConfig({
  plugins: [
    {
      resolve: "costplus-medusa",
      options: {},
    },
  ],
  modules: [
    {
      resolve: "@medusajs/medusa/payment",
      options: {
        providers: [
          {
            resolve: "costplus-medusa/providers/costplus",
            id: "costplus",
            options: {},
          },
        ],
      },
    },
  ],
})

Restart the Medusa backend so the new plugin and provider are loaded.

3. Configure in Admin

In the Medusa Admin, open Cost+ (route /app/costplus) and configure:

  • API Key — your merchant API key from the Merchant Portal (Websites → your website → Integration)
  • Checkout Expiry — timeout in minutes (sent to Cost+ as ISO-8601 duration, e.g. 5PT5M, default is 5 minutes)
  • Manual Capture — optional, authorize card payments without immediate capture
  • Debug Logging — optional, log API requests and responses for troubleshooting

On the same Cost+ Admin page, enable the checkout methods you want to expose for each region:

  • Cost+ Credit / Debit Card
  • Cost+ Apple Pay
  • Cost+ Google Pay
  • Cost+ Vipps MobilePay

Only activate the payment methods you have been approved for and received confirmation for.

Toggling methods on this page updates Medusa's region payment-provider links for Cost+ providers, so the standard Store API endpoint /store/payment-providers?region_id=... returns only the enabled Cost+ methods. If all Cost+ methods are disabled for a region, the Store API returns no Cost+ providers for that region.

4. Storefront Integration

When initiating a Cost+ payment session, pass the Medusa cart_id in data:

await sdk.store.payment.initiatePaymentSession(paymentCollection.id, {
  provider_id: "pp_costplus-credit-card_costplus",
  data: {
    cart_id: cart.id,
    return_url: `${origin}/${countryCode}/checkout/costplus/return?cart_id=${cart.id}`,
    failure_url: `${origin}/${countryCode}/checkout?step=payment&costplus_cancelled=1&cart_id=${cart.id}`,
  },
})

After the session is created, redirect the shopper to payment_session.data.costplus_payment_url.

The default success route is /checkout/costplus/return?cart_id={cart_id} — that page should call Medusa's complete-cart endpoint and then show the order confirmation page. The failure URL should return the shopper to the payment step without completing the cart.

Return and webhook handling always verifies the Cost+ order with GET /orders/{id}/ before mapping the payment state back to Medusa.

List Payment Methods from the Store API

List payment methods from Medusa's Store API instead of hardcoding the Cost+ provider IDs in your storefront. If the storefront caches /store/payment-providers, use no-store or invalidate the cache after Admin method changes so disabled methods disappear from checkout immediately.

For a reference implementation, see examples/nextjs/costplus-return/ in the costplus-medusa repository — a Next.js DTC starter return page that pairs a client handler with a server action so cart completion runs outside the page render and reliably redirects to /order/{id}/confirmed.

5. Webhooks

The plugin automatically registers a webhook endpoint at /hooks/payment/{identifier}_costplus. The Medusa backend URL must be publicly reachable over HTTPS so Cost+ can deliver asynchronous status updates.

If orders remain pending long after the configured expiry, that points to a webhook delivery or return-flow problem rather than the expiry setting itself.

6. Security & Settings Storage

The API key is encrypted before it is stored in the Medusa database. Set COSTPLUS_SETTINGS_SECRET in production:

COSTPLUS_SETTINGS_SECRET=your-strong-secret

If COSTPLUS_SETTINGS_SECRET is not set, the plugin falls back to Medusa's COOKIE_SECRET or JWT_SECRET.

Set COSTPLUS_API_KEY in the environment to lock the API key source — when set, the Admin page will not overwrite it. Leave it empty when merchants should manage the key in Admin.

7. Test and Launch

The Cost+ environment is determined by the API key — a sandbox website key runs in test mode, a production website key runs in live mode (no separate sandbox URL). Place a few test transactions to verify successful, cancelled, and expired flows before going live.

Support

Need help? Reach out to our support team at support@costplus.io.

On this page