npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zahls/medusa-plugin

v0.0.1

Published

zahls.ch payment provider for Medusa v2 (TWINT, cards, PostFinance).

Downloads

57

Readme

@zahls/medusa-plugin

NPM Version License Node.js Medusa

zahls.ch payment provider for Medusa v2.

This plugin lets a Medusa application create and manage zahls.ch Gateway checkouts from the backend. It supports:

  • Hosted checkout, where the customer is redirected to the zahls.ch payment page (session.data.link)
  • Swiss payment methods such as TWINT, cards, and PostFinance
  • Captures for authorized / reserved transactions
  • Refunds through zahls.ch transactions
  • Medusa's built-in payment webhook route for asynchronous status updates, with optional HMAC signature verification

The plugin never handles raw card data directly. zahls.ch credentials remain on the Medusa backend.

Compatibility

  • Medusa v2.18.x
  • zahls.ch Gateway API

Install

npm install @zahls/medusa-plugin

Configure Medusa

Register the plugin and payment provider in medusa-config.ts:

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

export default defineConfig({
  plugins: [
    {
      resolve: "@zahls/medusa-plugin",
      options: {},
    },
  ],
  modules: [
    {
      resolve: "@medusajs/medusa/payment",
      options: {
        providers: [
          {
            resolve: "@zahls/medusa-plugin/providers/zahls",
            id: "zahls",
            options: {
              apiKey: process.env.ZAHLS_API_KEY,
              instance: process.env.ZAHLS_INSTANCE,
              webhookSecret: process.env.ZAHLS_WEBHOOK_SECRET,
              successRedirectUrl: process.env.ZAHLS_SUCCESS_URL,
              failedRedirectUrl: process.env.ZAHLS_FAILED_URL,
              cancelRedirectUrl: process.env.ZAHLS_CANCEL_URL,
            },
          },
        ],
      },
    },
  ],
})

After the application starts, enable zahls.ch for the relevant region in Medusa Admin → Settings → Regions. Per Medusa's payment-provider model, the resulting provider identifier is pp_zahls_zahls when the service identifier is zahls and the configured provider id is zahls.

Configuration Options

| Option | Required | Description | | --- | --- | --- | | apiKey | Yes | Instance API secret from zahls.ch → API & Integrations. Keep it server-side. | | instance | Yes | Instance name (example for example.zahls.ch). | | webhookSecret | Yes | Signing key for X-Webhook-Signature verification. Webhooks are rejected without it. | | successRedirectUrl | No | Storefront URL after a successful payment. | | failedRedirectUrl | No | Storefront URL after a failed payment. | | cancelRedirectUrl | No | Storefront URL after the customer cancels. | | skipResultPage | No | Skip the zahls.ch result page (default true). |

Auth uses the X-API-KEY header (recommended by the zahls.ch / Payrexx REST API).

Hosted Checkout

The plugin creates a zahls.ch Gateway and stores the returned checkout link in the payment-session data. The storefront should redirect the customer to that URL:

const link = paymentSession.data?.link
if (typeof link === "string") {
  window.location.href = link
}

Use backend / webhook state as the source of truth. The storefront should not treat the redirect alone as proof of payment success.

When available, customer name, email, company, and billing address from the Medusa payment context are prefilled on the Gateway.

Webhooks

Medusa provides a built-in webhook listener route for payment providers at:

/hooks/payment/[identifier]_[provider]

For this plugin, with service identifier zahls and provider id: "zahls", add this URL in the zahls.ch merchant backend (Webhooks), with JSON content type:

https://your-medusa-backend.com/hooks/payment/zahls_zahls

The plugin verifies X-Webhook-Signature when webhookSecret is set, loads the Gateway from zahls.ch, maps the status to a Medusa payment action, and returns the payment session reference (referenceId) back to Medusa.

| zahls.ch status | Medusa webhook action | | --- | --- | | confirmed | captured | | authorized / reserved | authorized | | waiting | pending_authorization | | cancelled | canceled | | failed / declined | failed |

referenceId on the Gateway is set to the Medusa payment session id so webhooks can resolve the session.

What the Plugin Stores

The payment-session data returned by the provider includes:

  • id — zahls.ch Gateway id
  • hash
  • link — hosted checkout URL
  • referenceId — Medusa payment session id
  • status
  • amount and currency
  • transactionId when available
  • lastRefundId after a refund

Current Behavior and Limitations

  • Checkout is hosted-redirect only. There is no embedded card widget mode.
  • authorizePayment checks the remote zahls.ch Gateway status rather than performing a separate authorization step.
  • capturePayment succeeds immediately when the Gateway is already confirmed. For authorized / reserved / uncaptured, it calls zahls.ch capture (and falls back to charge if needed).
  • updatePayment recreates the Gateway when amount or currency changes before payment.
  • Refunds require a successful zahls.ch transaction id on the session.
  • webhookSecret is required; unsigned webhooks are rejected.

Sandbox Checklist

  • Create a Gateway and complete one successful hosted checkout (e.g. TWINT or card).
  • Confirm the storefront redirect to session.data.link works.
  • Verify at least one webhook-driven status update to Medusa.
  • Verify X-Webhook-Signature rejection when the secret is wrong.
  • Capture an authorized / reserved payment if your zahls.ch flow supports it.
  • Verify one full refund and one partial refund.
  • Verify canceled and failed checkouts map cleanly back into Medusa session state.

Local Development

npm run build
npm run dev
npm run test:unit
npm run test:integration:modules

Module integration tests need PostgreSQL (DB_HOST, DB_USERNAME, DB_PASSWORD, DB_PORT). Defaults: localhost:5432, user postgres.

Publish locally with npx medusa plugin:publish, then in a Medusa app:

npx medusa plugin:add @zahls/medusa-plugin

License

MIT