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

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.2or 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_idwhen initiating the payment session
Supported Payment Methods
| Checkout Label | Medusa Provider Id | Cost+ Identifier |
|---|---|---|
| Credit / Debit Card | pp_costplus-credit-card_costplus | credit-card |
| Apple Pay | pp_costplus-apple-pay_costplus | apple-pay |
| Google Pay | pp_costplus-google-pay_costplus | google-pay |
| Vipps MobilePay | pp_costplus-vipps-mobilepay_costplus | vipps-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-medusaYou 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.
5→PT5M, 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 CardCost+ Apple PayCost+ Google PayCost+ 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-secretIf 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.