@gmisoftware/przelewy24-payments-medusa
v0.1.3
Published
Przelewy24 (P24) payment integration plugin for Medusa e-commerce with BLIK, cards, and general P24 support.
Readme
Przelewy24 Payments for Medusa
A comprehensive payment provider plugin that enables Przelewy24 payments on Medusa V2 projects.
Table of Contents
- Features
- Prerequisites
- Installation
- Configuration
- Usage
- Client-Side Integration
- Supported Payment Methods
- Payment Flows
- Webhook Configuration
- Extending the Plugin
- Local Development and Customization
- License
Features
Multiple Payment Methods: Supports a wide range of Przelewy24 payment methods including:
- BLIK (Regular and One-Click)
- Credit/Debit Cards
- Bank Transfers
- White Label Integration
Modular Architecture: Multiple services in a single module provider for easy management.
Webhook Support: Full support for Przelewy24 webhooks for real-time payment status updates.
TypeScript Support: Full TypeScript implementation with proper types.
Sandbox Mode: Built-in sandbox support for testing.
[!WARNING] > This plugin has not been tested on a live store. Please conduct thorough testing before using it in a production environment. GMI Software is not responsible for any missed or failed payments resulting from the use of this plugin. If you encounter any issues, please report them here.
Prerequisites
- Medusa server v2.4.0 or later
- Node.js v20 or later
- A Przelewy24 merchant account with API credentials.
[!NOTE] > You can get your API credentials from your Przelewy24 merchant panel
Installation
yarn add p24-medusa-pluginConfiguration
Add the provider to the @medusajs/payment module in your medusa-config.ts file:
import { Modules } from "@medusajs/framework/utils";
module.exports = defineConfig({
modules: [
{
resolve: "@medusajs/medusa/payment",
options: {
providers: [
{
resolve:
"@gmisoftware/przelewy24-payments-medusa/providers/przelewy24",
id: "przelewy24",
options: {
api_key: process.env.P24_API_KEY,
merchant_id: process.env.P24_MERCHANT_ID,
pos_id: process.env.P24_POS_ID,
crc: process.env.P24_CRC,
sandbox: process.env.P24_IS_SANDBOX,
frontend_url: process.env.MEDUSA_STORE_URL,
backend_url: process.env.MEDUSA_BACKEND_URL,
},
},
],
},
},
],
plugins: ["@gmisoftware/przelewy24-payments-medusa"],
});Configuration Options
| Option | Description | Required | Default |
| -------------- | -------------------------------------- | -------- | ----------------------- |
| merchant_id | P24 Merchant ID | Yes | - |
| pos_id | P24 POS ID | Yes | - |
| api_key | P24 API Key | Yes | - |
| crc | P24 CRC Key for signature verification | Yes | - |
| sandbox | Enable sandbox mode (true/false or "true"/"false") | No | false |
| card_channel | P24 channel for card-only registration | No | 4096 |
| visa_mobile_method_id | P24 method id for Visa Mobile | No | 198 |
| frontend_url | Frontend URL for customer redirects | No | http://localhost:3000 |
| backend_url | Backend URL for webhook notifications | No | http://localhost:9000 |
Environment Variables
Create or update your .env file with the following variables:
# P24 Configuration
P24_MERCHANT_ID=your_merchant_id
P24_POS_ID=your_pos_id
P24_API_KEY=your_api_key
P24_CRC=your_crc_key
# URL Configuration (use the same names as medusa-config.ts)
MEDUSA_STORE_URL=https://your-frontend-domain.com
MEDUSA_BACKEND_URL=https://your-backend-domain.com # public HTTPS — P24 webhooks hit this host
P24_IS_SANDBOX=falseUsage
Once installed and configured, the Przelewy24 payment methods will be available in your Medusa admin. To enable them, log in to your Medusa Admin, browse to Settings > Regions, add or edit a region and select the desired P24 providers from the dropdown.
Make sure that the selected payment methods are enabled in your Przelewy24 merchant panel as well.
Client-Side Integration
To integrate with your storefront, you'll need to implement the payment flow according to Przelewy24's and Medusa's documentation. Here's a basic example:
BLIK Payment
BLIK payments use a two-phase flow:
Phase 1: Create Payment Session
// Create payment session for BLIK
const paymentSession = await medusa.payment.createPaymentSession({
provider_id: "pp_p24-blik_przelewy24",
amount: 10000, // 100.00 PLN in grosze
currency_code: "PLN",
data: {
country: "PL", // Country code (defaults to "PL" if not provided)
language: "pl", // Language code (defaults to "pl" if not provided)
},
context: {
email: "[email protected]",
},
});
// Response includes session_id and token, but no redirect_url
console.log(paymentSession.data.session_id); // Use this for BLIK processingPhase 2: Process BLIK Code
// Store API (publishable key + cart context)
const blikResponse = await fetch("/store/payments/blik/charge", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-publishable-api-key": publishableKey,
},
body: JSON.stringify({
token: paymentSession.data.token,
blikCode: "123456",
payment_session_id: paymentSession.id,
}),
});Legacy route
POST /payments/blikremains as a deprecated wrapper.
Card Payment 2.0 (white-label iframe)
Per P24 Card Payment 2.0 docs:
// 1) Create payment session — returns tokenization fields (no transaction register yet)
const { payment_session } = await medusa.store.cart.createPaymentSession(cartId, {
provider_id: "pp_p24-cards_przelewy24",
});
const { merchant_id, session_id, card_tokenization_sign } =
payment_session.data;
// 2) Load P24 Card 2.0 SDK and render iframe
// https://{sandbox|secure}.przelewy24.pl/js/cardTokenizationIframe.min.js
// new Przelewy24CardTokenization(merchant_id, session_id, card_tokenization_sign)
// .render("form", "#container", { lang: "pl", ... })
// On Pay: .tokenize("temporary") → listen for postMessage success with data.refId
// 3) Register with refId, then run P24 whitelabel charge script in the browser
const cardResponse = await fetch("/store/payments/card/charge", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-publishable-api-key": publishableKey,
},
body: JSON.stringify({
ref_id: refIdFromTokenization,
payment_session_id: payment_session.id,
}),
});
const { token, chargeScriptUrl } = await cardResponse.json();
// 4) Load chargeScriptUrl and run Przelewy24CardWhileLabelHandler (3DS in merchant modal)
// 5) Poll POST /store/carts/{cartId}/complete until type === "order"
// 6) Capture happens via P24 webhook to urlStatus (not via a separate confirm API)Visa Mobile (redirect with pre-selected method)
// 1) Create payment session — returns redirect_url with method 198 pre-selected
const { payment_session } = await medusa.store.cart.createPaymentSession(cartId, {
provider_id: "pp_p24-visa-mobile_przelewy24",
});
// 2) Redirect customer to P24 Visa Mobile flow
window.location.href = payment_session.data.redirect_url;
// 3) Customer completes payment on P24 hosted page
// 4) Return to frontend_url / return_url
// 5) Capture happens via P24 webhook to urlStatus (not via a separate confirm API)Transaction status (poll timeout fallback)
await fetch("/store/payments/transaction/status", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
session_id: payment_session.data.session_id,
provider_id: payment_session.provider_id,
}),
});General P24 Payment
// Create payment session for general P24
const paymentSession = await medusa.payment.createPaymentSession({
provider_id: "pp_p24-provider_przelewy24",
amount: 10000,
currency_code: "PLN",
data: {
country: "PL", // Country code (defaults to "PL" if not provided)
language: "pl", // Language code (defaults to "pl" if not provided)
},
context: {
email: "[email protected]",
},
});
// Redirect user to P24 payment selection
window.location.href = paymentSession.data.redirect_url;Supported Payment Methods
The plugin currently supports the following Przelewy24 payment methods:
| Payment Method | Provider ID | Notes |
| -------------- | ----------------------------------- | ------------------------------ |
| BLIK | pp_p24-blik_przelewy24 | White-label, channel 64 |
| Cards | pp_p24-cards_przelewy24 | White-label iframe, channel 4096 |
| Visa Mobile | pp_p24-visa-mobile_przelewy24 | Redirect with method 198 |
| General P24 | pp_p24-provider_przelewy24 | Redirect to P24 method picker |
Payment Flows
1. BLIK (white-label)
- Create payment session (
initiatePayment) →token,session_id - Customer enters 6-digit BLIK code
POST /store/payments/blik/charge→ P24chargeByCode- Customer approves in banking app
- Storefront polls
POST /store/carts/{id}/completeuntil order is created - P24 sends webhook to
urlStatus→ verify + capture →payment_collection.completed_at
BLIK does not use redirect URLs.
2. Cards (P24 Card 2.0 white-label)
- Create payment session → tokenization fields only (
merchant_id,session_id,card_tokenization_sign,amount_grosze) — notransaction/registeryet - Load
cardTokenizationIframe.min.js, render iframe; regulation checkbox is inside the P24 widget - On Pay:
tokenize("temporary")→refIdvia postMessage POST /store/payments/card/chargewithref_id→transaction/register+ returnschargeScriptUrl- Browser runs
Przelewy24CardWhileLabelHandler(3DS in merchant UI) - Storefront polls
POST /store/carts/{id}/completeuntil order is created - P24 webhook → verify + capture
See P24 Card 2.0 docs.
3. Visa Mobile (redirect with pre-selected method)
- Create payment session →
redirect_url(P24transaction/registerincludesmethod: 198) - Customer pays on P24 hosted Visa Mobile page
- Return to
frontend_url/return_url - Webhook confirms payment
4. General P24 (redirect)
- Create payment session →
redirect_url - Customer pays on P24 hosted page
- Return to
frontend_url/return_url - Webhook confirms payment
Completion model (all white-label methods)
| Step | Mechanism |
|------|-----------|
| Order creation | Storefront polls complete; Medusa authorizePayment queries P24 API; or complete-captured-carts-backstop (Medusa, every 2 min) when payment is already captured |
| Capture + completed_at | P24 webhook to urlStatus, or reconcile-p24-payments job (every 5 min) |
Do not add a separate store “confirm” route to replace webhooks. Production capture is webhook-driven.
Body Chief monorepo: see
Medusa/docs/P24_PAYMENTS.mdandMedusa/docs/adr/0009-p24-white-label-payments.md.
Webhook Configuration
Webhook URLs (auto-registered)
On each transaction/register, the plugin sets:
urlStatus: {backend_url}/hooks/payment/{service-identifier}_{payment-module-id}With payment-module config id: "przelewy24" (recommended):
| Method | Webhook URL |
|--------|-------------|
| Cards | {backend_url}/hooks/payment/p24-cards_przelewy24 |
| BLIK | {backend_url}/hooks/payment/p24-blik_przelewy24 |
| Visa Mobile | {backend_url}/hooks/payment/p24-visa-mobile_przelewy24 |
| General | {backend_url}/hooks/payment/p24-provider_przelewy24 |
Medusa route: POST /hooks/payment/:provider → getWebhookActionAndData → processPaymentWorkflow.
backend_urlmust be the Medusa server URL (public HTTPS in production), not the storefront.- Webhooks are registered per transaction; you do not paste these into the P24 panel for white-label flows.
- Return URL for redirects:
{frontend_url}(and cart-specificreturn_urlin session data).
Production checklist
- [ ]
backend_urlreachable from the internet (P24 cannot calllocalhost) - [ ]
crcmatches merchant panel (signature verification) - [ ] P24 webhook source IPs allowed (see
P24_WEBHOOK_ALLOWED_IPS; sandbox allows localhost) - [ ] Region enables correct
pp_p24-*_przelewy24providers
Local development
Without a tunnel, webhooks will not arrive. Orders may still be created via poll complete, but payment_collection can remain not_paid until the reconcile job runs (~5 min). Use ngrok + public backend_url for full webhook testing.
cd P24-package && yarn build && yalc publish --push # Body Chief: update Medusa pluginRelated documentation (Body Chief monorepo)
| Document | Audience | |----------|----------| | Medusa/docs/P24_PAYMENTS.md | Backend / ops runbook | | Medusa/docs/adr/0009-p24-white-label-payments.md | Architecture decisions | | Medusa/CART_PAYMENTS_AND_DISCOUNTS_GUIDE.md | Store API integration | | web/docs/P24_CHECKOUT.md | Next.js storefront |
Extending the Plugin
To add support for additional Przelewy24 payment methods, create a new service in src/providers/przelewy24/services that extends the P24Base class:
import P24Base from "../core/p24-base";
import { PaymentOptions } from "../types";
class P24NewMethodService extends P24Base {
static identifier = "p24-new-method";
get paymentCreateOptions(): PaymentOptions {
return {
method: "new_method",
};
}
}
export default P24NewMethodService;Make sure to replace new_method with the actual Przelewy24 payment method ID.
Export your new service from src/providers/przelewy24/services/index.ts. Then add your new service to the list of services in src/providers/przelewy24/index.ts.
Local development and customization
In case you want to customize and test the plugin locally, refer to the Medusa Plugin docs.
License
MIT License
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
