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

@propeller-commerce/propeller-v2-mollie

v0.2.3

Published

Mollie PSP payment provider for the Propeller eCommerce V2 platform — drives Mollie payments through the Propeller GraphQL API via @propeller-commerce/propeller-sdk-v2. Server-side, framework-agnostic.

Readme

@propeller-commerce/propeller-v2-mollie

Mollie PSP payment provider for the Propeller eCommerce V2 platform. Drives Mollie payments through the Propeller GraphQL API via @propeller-commerce/propeller-sdk-v2.

This is a server-side, framework-agnostic library: it creates Mollie payments, persists them in Propeller, reconciles order/payment state from the Mollie webhook, and exposes a live status lookup for the return page. The host owns the HTTP layer (the checkout route, the webhook route, and the return/thank-you page).

⚠️ Server-only. This package handles secret Mollie API keys and issues privileged Propeller mutations. Never bundle it into browser code.

Install

npm install @propeller-commerce/propeller-v2-mollie @propeller-commerce/propeller-sdk-v2

@propeller-commerce/propeller-sdk-v2 is a peer dependency — the host provides the configured GraphQL client.

The three inputs

new MollieProvider(
  { liveApiKey, testApiKey, testMode },  // ← the 3 PSP inputs
  { client, webhookUrl, logger? }        // ← host wiring
);

| Input | Meaning | |---|---| | liveApiKey | Mollie live API key (live_…). Used when testMode is false. | | testApiKey | Mollie test API key (test_…). Used when testMode is true. | | testMode | true → test key, false → live key. |

Host wiring (client, webhookUrl, logger)

The package does not build a Propeller client and holds no Propeller credentials — you inject a configured SDK GraphQLClient.

import { GraphQLClient } from '@propeller-commerce/propeller-sdk-v2';

const client = new GraphQLClient({
  endpoint: process.env.PROPELLER_GRAPHQL_ENDPOINT!,
  apiKey: process.env.PROPELLER_API_KEY!,
  orderEditorApiKey: process.env.PROPELLER_ORDER_EDITOR_API_KEY!, // ← REQUIRED, see below
  securityMode: 'direct',
});

❗ You MUST set orderEditorApiKey

In direct mode the SDK routes order-editor mutations — orderSetStatus and triggerOrderSendConfirm — to the orderEditorApiKey header instead of apiKey. This package updates order status on every webhook, so without orderEditorApiKey the order-status update silently auth-fails while the payment update appears to succeed. Always supply it.

webhookUrl is the public URL Mollie will POST to (Mollie cannot reach localhost; use a tunnel like cloudflared or ngrok in development). logger is optional and defaults to a console-backed logger.

Usage

1. Start a payment (checkout)

Creates the Mollie payment and the Propeller payment (status OPEN + an AUTHORIZATION transaction), then returns the Mollie checkout URL.

import { MollieProvider } from '@propeller-commerce/propeller-v2-mollie';

const provider = new MollieProvider(
  {
    liveApiKey: process.env.MOLLIE_LIVE_KEY!,
    testApiKey: process.env.MOLLIE_TEST_KEY!,
    testMode: process.env.MOLLIE_TEST_MODE === 'true',
  },
  { client, webhookUrl: `${process.env.PUBLIC_BASE_URL}/api/mollie/webhook` }
);

const { checkoutUrl, paymentId } = await provider.createPayment({
  orderId: 12345,
  amount: '49.95',         // major units (number or decimal string)
  currency: 'EUR',
  method: 'ideal',         // Propeller method code → mapped to a Mollie method
  description: 'Order #12345',
  redirectUrl: `${process.env.PUBLIC_BASE_URL}/checkout/thank-you/12345`,
  userId: 678,             // or anonymousId for guests
});

// Redirect the shopper to `checkoutUrl`.

2. Handle the webhook

Mollie POSTs only { id }. Pass that id to handleWebhook; it re-fetches the payment from Mollie (the body is never trusted beyond the id), classifies it, and updates the Propeller payment + order. It never throws — always return 200 to Mollie so it doesn't enter a retry loop.

Next.js (App Router route handler)

// app/api/mollie/webhook/route.ts
import { NextResponse } from 'next/server';
import { provider } from '@/lib/mollie'; // your singleton MollieProvider

export async function POST(req: Request) {
  const body = new URLSearchParams(await req.text()); // Mollie posts form-encoded
  const id = body.get('id') ?? '';
  await provider.handleWebhook(id);     // result is logged internally
  return new NextResponse(null, { status: 200 }); // always 200
}

Express

import express from 'express';
import { provider } from './mollie';

const app = express();
app.use(express.urlencoded({ extended: false })); // Mollie posts form-encoded

app.post('/api/mollie/webhook', async (req, res) => {
  await provider.handleWebhook(req.body.id ?? '');
  res.sendStatus(200); // always 200
});

3. Resolve the outcome on the return page

Mollie redirects the shopper back to your redirectUrl for every outcome — paid, open, failed, canceled, expired all land on the same URL — and the webhook that finalizes the order is asynchronous, so it may not have arrived yet. The order status alone therefore can't tell open from failed (both are UNFINISHED). Ask Mollie directly with getPaymentStatus:

const result = await provider.getPaymentStatus(paymentId);
// { ok, paymentId, status?, settled?, orderId? }

It is read-only (never touches Propeller) and always resolves. Use it to pick the return-page UI and decide the local cart action:

| Mollie status | settled | Return-page UI | Local cart | |---|---|---|---| | paid / authorized | true | Success | clear it | | open / pending | false | "Payment still open" + re-check | keep it | | failed / canceled / expired | false | Failure + retry | keep it |

settled is the cart hint: clear the local cart only for a captured payment (paid/authorized). An open/pending payment isn't finalized yet, so keeping the local cart leaves it in sync with the still-live backend cart; clearing it early would let a subsequent "add to cart" silently reuse the same un-finalized order. The same rule is exported as a pure helper:

import { isSettledStatus } from '@propeller-commerce/propeller-v2-mollie';
isSettledStatus('paid');   // true  → clear the local cart
isSettledStatus('open');   // false → keep it
isSettledStatus('failed'); // false → keep it

Two distinct cart rules. isSettledStatus / getPaymentStatus().settled is the client / return-page rule for the shopper's local cart. The webhook's "clears cart" column below is a separate, server-side rule for the backend cart — it intentionally clears on open/pending too. Don't conflate them.

Status mapping (webhook → Propeller)

The webhook classifies the Mollie payment and pushes this status tuple to Propeller:

| Mollie state | txn type | txn status | payment status | order status | clears cart | |---|---|---|---|---|---| | paid (no refund/chargeback) | PAY | SUCCESS | PAID | NEW | ✔ | | open | PAY | OPEN | OPEN | UNFINISHED | ✔ | | pending | PAY | PENDING | PENDING | UNFINISHED | ✔ | | failed | PAY | FAILED | FAILED | UNFINISHED | ✗ | | expired | PAY | FAILED | EXPIRED | UNFINISHED | ✗ | | canceled | PAY | FAILED | CANCELLED | UNFINISHED | ✗ | | has refunds | REFUND | SUCCESS | PAID | NEW | ✔ | | has chargebacks | CHARGEBACK | SUCCESS | CHARGEBACK | NEW | ✔ |

On the "clears cart" rows the package also sends the order confirmation email, fires the confirm event, and attaches the order PDF — via a single orderSetStatus mutation. Amounts are converted from Mollie's decimal string to Propeller's integer cents; the webhook uses Mollie's authoritative amount.

This "clears cart" column is the server-side / backend cart and is separate from the local cart rule the return page uses (isSettledStatus — see step 3), which keeps the local cart on open/pending.

Public API

| Export | Description | |---|---| | MollieProvider | The provider class — createPayment, handleWebhook, getPaymentStatus. | | isSettledStatus | Pure helper — true only for paid/authorized (the local-cart rule). | | MollieProviderConfig, MollieProviderHost, CreatePaymentArgs, CreatePaymentResult, HandleWebhookResult, MolliePaymentStatus, PaymentStatusResult | Types. | | OrderStatus | Propeller order-status string consts. | | PaymentStatuses, TransactionTypes, TransactionStatuses | Re-exported SDK enums. | | resolveMollieMethod, classifyPayment, toCents, toMollieValue | Pure helpers (advanced / testing). | | consoleLogger, noopLogger, Logger, LogLevel | Logging. |

Notes & limitations (v1)

  • Refund/chargeback initiation is out of scope — the package only reacts to Mollie's refund/chargeback webhooks.
  • Uses the Mollie Payments API (not the Orders API).
  • Webhook idempotency: Mollie may re-deliver. Payment update by paymentId is idempotent for status; transactions are keyed by transactionId (the Mollie payment id) — confirm your backend dedups appended transactions if re-delivery matters to you.

License

MIT