@gmisoftware/przelewy24-payments-medusa
Version:
Przelewy24 (P24) payment integration plugin for Medusa e-commerce with BLIK, cards, and general P24 support.
424 lines (319 loc) • 15.9 kB
Markdown
# Przelewy24 Payments for Medusa
A comprehensive payment provider plugin that enables [Przelewy24](https://www.przelewy24.pl/) payments on [Medusa](https://medusajs.com/) V2 projects.
<p align="center">
<a href="https://gmi.software/">
<img src="https://img.shields.io/badge/Website-gmi.software-blue?style=flat&logo=world" alt="Website" />
</a>
<a href="https://pl.linkedin.com/company/gmisoftware">
<img src="https://img.shields.io/badge/LinkedIn-gmisoftware-blue?style=flat&logo=linkedin" alt="LinkedIn" />
</a>
</p>
## Table of Contents
- [Features](#features)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Configuration Options](#configuration-options)
- [Environment Variables](#environment-variables)
- [Usage](#usage)
- [Client-Side Integration](#client-side-integration)
- [Supported Payment Methods](#supported-payment-methods)
- [Payment Flows](#payment-flows)
- [Webhook Configuration](#webhook-configuration)
- [Extending the Plugin](#extending-the-plugin)
- [Local Development and Customization](#local-development-and-customization)
- [License](#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](https://github.com/gmi-software/p24-medusa-plugin/issues)._
## Prerequisites
- Medusa server v2.4.0 or later
- Node.js v20 or later
- A [Przelewy24](https://www.przelewy24.pl/) merchant account with API credentials.
> [!NOTE] > _You can get your API credentials from your Przelewy24 merchant panel_
## Installation
```bash
yarn add p24-medusa-plugin
```
## Configuration
Add the provider to the `@medusajs/payment` module in your `medusa-config.ts` file:
```typescript
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:
```bash
# 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=false
```
## Usage
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**
```typescript
// 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: "customer@example.com",
},
});
// Response includes session_id and token, but no redirect_url
console.log(paymentSession.data.session_id); // Use this for BLIK processing
```
**Phase 2: Process BLIK Code**
```typescript
// 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/blik` remains as a deprecated wrapper.
#### Card Payment 2.0 (white-label iframe)
Per [P24 Card Payment 2.0 docs](https://developers.przelewy24.pl/extended/index.php?pl#tag/Inicjalizacja-formularza):
```typescript
// 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)
```typescript
// 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)
```typescript
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
```typescript
// 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: "customer@example.com",
},
});
// 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)
1. Create payment session (`initiatePayment`) → `token`, `session_id`
2. Customer enters 6-digit BLIK code
3. `POST /store/payments/blik/charge` → P24 `chargeByCode`
4. Customer approves in banking app
5. Storefront polls `POST /store/carts/{id}/complete` until order is created
6. 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)
1. Create payment session → tokenization fields only (`merchant_id`, `session_id`, `card_tokenization_sign`, `amount_grosze`) — **no** `transaction/register` yet
2. Load `cardTokenizationIframe.min.js`, render iframe; regulation checkbox is **inside** the P24 widget
3. On Pay: `tokenize("temporary")` → `refId` via postMessage
4. `POST /store/payments/card/charge` with `ref_id` → `transaction/register` + returns `chargeScriptUrl`
5. Browser runs `Przelewy24CardWhileLabelHandler` (3DS in merchant UI)
6. Storefront polls `POST /store/carts/{id}/complete` until order is created
7. P24 webhook → verify + capture
See [P24 Card 2.0 docs](https://developers.przelewy24.pl/extended/index.php?pl#tag/Inicjalizacja-formularza).
### 3. Visa Mobile (redirect with pre-selected method)
1. Create payment session → `redirect_url` (P24 `transaction/register` includes `method: 198`)
2. Customer pays on P24 hosted Visa Mobile page
3. Return to `frontend_url` / `return_url`
4. Webhook confirms payment
### 4. General P24 (redirect)
1. Create payment session → `redirect_url`
2. Customer pays on P24 hosted page
3. Return to `frontend_url` / `return_url`
4. 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.md` and `Medusa/docs/adr/0009-p24-white-label-payments.md`.
## Webhook Configuration
### Webhook URLs (auto-registered)
On each `transaction/register`, the plugin sets:
```text
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_url`** must 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-specific `return_url` in session data).
### Production checklist
- [ ] `backend_url` reachable from the internet (P24 cannot call `localhost`)
- [ ] `crc` matches merchant panel (signature verification)
- [ ] P24 webhook source IPs allowed (see `P24_WEBHOOK_ALLOWED_IPS`; sandbox allows localhost)
- [ ] Region enables correct `pp_p24-*_przelewy24` providers
### 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.
```bash
cd P24-package && yarn build && yalc publish --push # Body Chief: update Medusa plugin
```
## Related documentation (Body Chief monorepo)
| Document | Audience |
|----------|----------|
| [Medusa/docs/P24_PAYMENTS.md](../Medusa/docs/P24_PAYMENTS.md) | Backend / ops runbook |
| [Medusa/docs/adr/0009-p24-white-label-payments.md](../Medusa/docs/adr/0009-p24-white-label-payments.md) | Architecture decisions |
| [Medusa/CART_PAYMENTS_AND_DISCOUNTS_GUIDE.md](../Medusa/CART_PAYMENTS_AND_DISCOUNTS_GUIDE.md) | Store API integration |
| [web/docs/P24_CHECKOUT.md](../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:
```typescript
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](https://docs.medusajs.com/learn/fundamentals/plugins/create#3-publish-plugin-locally-for-development-and-testing).
## License
MIT License
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Submit a pull request