PHPackages                             italiamultimedia/xpay-web - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. italiamultimedia/xpay-web

ActiveLibrary[Payment Processing](/categories/payments)

italiamultimedia/xpay-web
=========================

A PHP component/library.

v0.1.2(1mo ago)04↓50%MITPHPPHP ^8.4

Since Jun 15Pushed 1mo agoCompare

[ Source](https://github.com/italiamultimedia/xpay-web)[ Packagist](https://packagist.org/packages/italiamultimedia/xpay-web)[ Docs](https://github.com/italiamultimedia/xpay-web)[ RSS](/packages/italiamultimedia-xpay-web/feed)WikiDiscussions main Synced 2w ago

READMEChangelogDependencies (46)Versions (4)Used By (0)

italiamultimedia/xpay-web
=========================

[](#italiamultimediaxpay-web)

PHP library for Nexi XPay Web / Phoenix API-key integrations.

This package targets the API-key based XPay Web endpoints, not the legacy Alias + MAC integration.

Implemented functionality:

- Hosted Payment Page creation: `POST /orders/hpp`
- Recurring contract creation through Hosted Payment Page recurrence data
- Subsequent recurring payments: `POST /orders/mit`
- Order status retrieval: `GET /orders/{orderId}`
- Hosted payment result handling
- Hosted payment notification parsing
- Nexi API error parsing
- Customer-facing Nexi error messages in English and Italian

Installation
------------

[](#installation)

```
composer require italiamultimedia/xpay-web
```

The HTTP payment services need:

- a PSR-18 HTTP client
- PSR-17 request and stream factories

For a plain PHP project, one simple combination is:

```
composer require php-http/curl-client nyholm/psr7
```

Frameworks may already provide these services; in that case, pass your framework's PSR implementations instead.

Configuration
-------------

[](#configuration)

You need two values from your application configuration:

- your Nexi API key
- the Nexi environment: `test` or `production`

The environment matters because it decides which Nexi API base URL is used:

- `Configuration::ENVIRONMENT_TEST` uses the Nexi sandbox API
- `Configuration::ENVIRONMENT_PRODUCTION` uses the live Nexi API

Use the sandbox environment with a sandbox API key. Use the production environment only with a production API key.

Example `.env` values:

```
XPAY_API_KEY=your-api-key
XPAY_ENVIRONMENT=test
```

This package does not read `.env` files directly. Read those values with your framework/configuration layer, then pass them into `PaymentSystemSettings`.

Plain PHP example:

```
use ItaliaMultimedia\XPayWeb\DataTransfer\Configuration;
use ItaliaMultimedia\XPayWeb\DataTransfer\PaymentSystemSettings;

$apiKey = (string) getenv('XPAY_API_KEY');
$environment = (string) (getenv('XPAY_ENVIRONMENT') ?: Configuration::ENVIRONMENT_TEST);

$paymentSystemSettings = new PaymentSystemSettings($apiKey, $environment);
```

You can also pass the strings directly:

```
$paymentSystemSettings = new PaymentSystemSettings($apiKey, 'test');
```

Using `Configuration::ENVIRONMENT_TEST` and `Configuration::ENVIRONMENT_PRODUCTION` is preferred because it avoids typos.

Create Services
---------------

[](#create-services)

Create one `DependencyContainer` with your payment settings, then use `PaymentServiceFactory` to build services.

```
use Http\Client\Curl\Client;
use ItaliaMultimedia\XPayWeb\Container\DependencyContainer;
use ItaliaMultimedia\XPayWeb\Factory\Service\PaymentServiceFactory;
use Nyholm\Psr7\Factory\Psr17Factory;

$dependencyContainer = new DependencyContainer($paymentSystemSettings);
$paymentServiceFactory = new PaymentServiceFactory($dependencyContainer);

$psr17Factory = new Psr17Factory();

$simplePaymentService = $paymentServiceFactory->createSimplePaymentService(
    new Client(),
    $psr17Factory,
    $psr17Factory,
);

$recurringPaymentService = $paymentServiceFactory->createRecurringPaymentService(
    new Client(),
    $psr17Factory,
    $psr17Factory,
);

$hostedPaymentResultService = $paymentServiceFactory->createHostedPaymentResultService();
```

Create A Hosted Payment Page
----------------------------

[](#create-a-hosted-payment-page)

Create an order in your application first, then send Nexi the hosted payment page request.

```
use ItaliaMultimedia\XPayWeb\DataTransfer\Configuration;
use ItaliaMultimedia\XPayWeb\DataTransfer\Request\CreateHostedPaymentPageOptions;
use ItaliaMultimedia\XPayWeb\DataTransfer\Request\CreateHostedPaymentPageRequest;

$request = new CreateHostedPaymentPageRequest(
    $correlationId,
    $orderId,
    1000,
    Configuration::CURRENCY,
    'ITA',
    'https://example.com/payment/result',
    'https://example.com/payment/cancel',
    new CreateHostedPaymentPageOptions(description: 'Order description'),
);

$response = $simplePaymentService->createHostedPaymentPage($request);

// Store this with your local order if you use hosted payment notifications.
$securityToken = $response->securityToken;

header(sprintf('Location: %s', $response->hostedPage));
exit;
```

Notes:

- `correlationId` must be a UUID v4.
- `orderId` should be your local payment/order identifier.
- Amounts are expressed in minor units, so `1000` means EUR 10.00 when using `Configuration::CURRENCY`.
- `resultUrl` is where Nexi redirects the customer after payment.
- `cancelUrl` is where Nexi redirects the customer after cancellation.
- `notificationUrl` is optional. Pass `null` unless you have a real public HTTPS webhook listener.
- `recurrence` is optional. Pass it only when this hosted payment should create a recurring payment contract.

If you do have a webhook listener:

```
$request = new CreateHostedPaymentPageRequest(
    $correlationId,
    $orderId,
    1000,
    Configuration::CURRENCY,
    'ITA',
    'https://example.com/payment/result',
    'https://example.com/payment/cancel',
    new CreateHostedPaymentPageOptions(
        notificationUrl: 'https://example.com/payment/notification',
        description: 'Order description',
    ),
);
```

Verify Payment Status
---------------------

[](#verify-payment-status)

After the customer returns to your `resultUrl`, do not trust only the redirect parameters. Verify the order through Nexi:

```
use ItaliaMultimedia\XPayWeb\DataTransfer\Request\RetrieveOrderStatusRequest;

$response = $simplePaymentService->retrieveOrderStatus(
    new RetrieveOrderStatusRequest($correlationId, $orderId),
);

foreach ($response->operations as $operation) {
    // Inspect operationResult, operationType, operationId, operationTime, etc.
}
```

The result redirect may include `paymentId`, but this library treats your local `orderId` as the source of truth.

Hosted Result And Notifications
-------------------------------

[](#hosted-result-and-notifications)

The result URL should already be tied to the local order.

```
$result = $hostedPaymentResultService->createHostedPaymentResult($orderId);
```

For server-to-server notifications, Nexi posts JSON to your `notificationUrl`. Parse that received payload and validate it with the security token you stored when creating the hosted payment page:

```
$notification = $hostedPaymentResultService->parseHostedPaymentNotification(
    $notificationData,
    $expectedSecurityToken,
);
```

For notification-only parsing, `new DependencyContainer()` is enough because payment settings are not needed.

Error Handling
--------------

[](#error-handling)

API errors are thrown as `NexiApiException`.

```
use ItaliaMultimedia\XPayWeb\Service\Error\NexiErrorMessageService;
use ItaliaMultimedia\XPayWeb\Service\Exception\NexiApiException;

try {
    $response = $simplePaymentService->createHostedPaymentPage($request);
} catch (NexiApiException $exception) {
    $technicalMessage = $exception->getMessage();
    $customerMessage = (new NexiErrorMessageService())->getCustomerMessage($exception, 'it');
}
```

Supported customer message language codes are `en` and `it`. Unknown languages fall back to English.

Recurring Hosted Payments
-------------------------

[](#recurring-hosted-payments)

Nexi recurring payments are MIT payments: Merchant Initiated Transactions. The first customer-present payment creates a contract, then later charges can be made against that contract.

This package supports both phases:

- first payment through Hosted Payment Page with a `recurrence` object inside `CreateHostedPaymentPageRequest`
- later merchant-initiated charges through `POST /orders/mit`

For the first payment, Nexi still receives the same `POST /orders/hpp` request; the recurrence data tells Nexi to create a contract from the card used on the hosted page.

Use `MIT_SCHEDULED` when the later merchant charges have a defined schedule, for example every 30 days or on the first day of each month. Nexi also accepts `contractExpiryDate` and `contractFrequency` for scheduled contracts.

Use `MIT_UNSCHEDULED` when the later merchant charges do not have a fixed schedule, for example usage-based or variable-date billing.

Scheduled example:

```
use ItaliaMultimedia\XPayWeb\DataTransfer\Configuration;
use ItaliaMultimedia\XPayWeb\DataTransfer\Request\CreateHostedPaymentPageOptions;
use ItaliaMultimedia\XPayWeb\DataTransfer\Request\CreateHostedPaymentPageRequest;
use ItaliaMultimedia\XPayWeb\DataTransfer\Request\HostedPaymentPageRecurrence;

$contractId = 'CUSTOMER123PLAN1';

$request = new CreateHostedPaymentPageRequest(
    $correlationId,
    $orderId,
    1000,
    Configuration::CURRENCY,
    'ITA',
    'https://example.com/payment/result',
    'https://example.com/payment/cancel',
    new CreateHostedPaymentPageOptions(
        notificationUrl: 'https://example.com/payment/notification',
        description: 'Subscription first payment',
        recurrence: HostedPaymentPageRecurrence::createMitScheduled($contractId, '2027-12-31', '30'),
    ),
);
```

Unscheduled example:

```
$request = new CreateHostedPaymentPageRequest(
    $correlationId,
    $orderId,
    1000,
    Configuration::CURRENCY,
    'ITA',
    'https://example.com/payment/result',
    'https://example.com/payment/cancel',
    new CreateHostedPaymentPageOptions(
        description: 'Usage based first payment',
        recurrence: HostedPaymentPageRecurrence::createMitUnscheduled($contractId),
    ),
);
```

Store the `contractId` in your application together with the customer/subscription. Nexi expects it to be unique in your merchant domain.

Subsequent recurring charges are the second phase of Nexi recurring payments and use `POST /orders/mit`. They are server-to-server calls and do not redirect the customer back to the hosted page.

```
use ItaliaMultimedia\XPayWeb\DataTransfer\Configuration;
use ItaliaMultimedia\XPayWeb\DataTransfer\Request\CreateSubsequentRecurringPaymentOptions;
use ItaliaMultimedia\XPayWeb\DataTransfer\Request\CreateSubsequentRecurringPaymentRequest;

$response = $recurringPaymentService->createSubsequentRecurringPayment(
    new CreateSubsequentRecurringPaymentRequest(
        $correlationId,
        $idempotencyKey,
        $orderId,
        1000,
        Configuration::CURRENCY,
        $contractId,
        new CreateSubsequentRecurringPaymentOptions(
            CreateSubsequentRecurringPaymentOptions::CAPTURE_TYPE_IMPLICIT,
            $customerId,
            'Subscription renewal',
        ),
    ),
);

$operation = $response->operation;
```

For subsequent payments:

- `correlationId` must be a UUID v4.
- `idempotencyKey` must be a UUID v4 and should be unique for that payment attempt. Reuse the same key only when retrying the same charge after a transport failure.
- `orderId` is the new merchant order/payment identifier for the recurring charge.
- `contractId` is the contract created during the first hosted payment.
- `captureType` is optional. Use `CAPTURE_TYPE_IMPLICIT` for automatic confirmation or `CAPTURE_TYPE_EXPLICIT` for authorization only, if your terminal configuration allows it.

Manual Sandbox Scripts
----------------------

[](#manual-sandbox-scripts)

```
php bin/create-hosted-payment-page-test.php
php bin/create-recurring-hosted-payment-page-test.php
php bin/retrieve-order-status-test.php  [correlationId]
```

The sandbox scripts do not test server-to-server notifications. To test notification parsing, point `notificationUrl` at a real listener in your application and pass the received payload to `HostedPaymentResultService::parseHostedPaymentNotification()`.

Development
-----------

[](#development)

```
composer check:lint
composer check:phpcs
composer check:phpstan
composer check:phan
composer check:phpmd
composer check:psalm
composer check
composer test
```

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance93

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 75% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~5 days

Total

3

Last Release

36d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/d141a82e41201dab8f836df9f4c78ad56a131c100c1ff814515d0d6f011fdecd?d=identicon)[ariademur](/maintainers/ariademur)

---

Top Contributors

[![ariademur](https://avatars.githubusercontent.com/u/34913107?v=4)](https://github.com/ariademur "ariademur (36 commits)")[![punkrock34](https://avatars.githubusercontent.com/u/52674665?v=4)](https://github.com/punkrock34 "punkrock34 (12 commits)")

---

Tags

nexiitaliamultimediaxpay-web

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Psalm

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/italiamultimedia-xpay-web/health.svg)

```
[![Health](https://phpackages.com/badges/italiamultimedia-xpay-web/health.svg)](https://phpackages.com/packages/italiamultimedia-xpay-web)
```

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k17](/packages/tempest-framework)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

85036.3k](/packages/flow-php-flow)[cakephp/cakephp

The CakePHP framework

8.8k19.5M1.8k](/packages/cakephp-cakephp)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6942.5M426](/packages/drupal-core-recommended)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[chargebee/chargebee-php

ChargeBee API client implementation for PHP

758.5M9](/packages/chargebee-chargebee-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
