sol-parts/payum-hutko

Payum gateway for Hutko acquiring (Fondy-compatible protocol): hosted checkout, preauth hold payments and refunds

Maintainers

Package info

github.com/sol-parts/payum-hutko

Homepage

pkg:composer/sol-parts/payum-hutko

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-18 16:03 UTC

This package is auto-updated.

Last update: 2026-08-18 16:06:39 UTC


README

Provides Hutko acquiring integration for Payum: hosted checkout with instant debit or two-stage preauth hold payments, and refunds. Hutko speaks the Fondy-compatible protocol 1.0 (JSON, SHA1 merchant signature).

Supported operations:

  • Capture — create a payment (/api/checkout/url/) and redirect the customer to the Hutko payment page (checkout_url).
  • Authorize — same flow with preauth=Y: the funds are locked on the customer's card and the store settles the payment later.
  • DoCapture (the shared request from sol-parts/payum-contracts, when installed) — settle a preauth hold via /api/capture/order_id/, full or partial amount.
  • Cancel — reversal of an active preauth hold via /api/reverse/order_id/. For anything else it is a no-op: a captured payment is returned by Refund, and a not-yet-paid order simply expires on its own.
  • Refund — refund of a paid order via /api/reverse/order_id/, full transaction amount.
  • Notify — the postback (server_callback_url) is treated as a sync trigger only: the state is always re-fetched server-to-server, so a forged callback can at most trigger an extra sync. Hutko considers a postback delivered only on HTTP 200 and retries otherwise — the action always answers 200.
  • Sync — re-fetch the order state via /api/status/order_id/.
  • GetStatus — maps Hutko order statuses to Payum ones (see the table below).

Payment flow

sequenceDiagram
    autonumber
    actor C as Customer
    participant S as Store
    participant H as Hutko

    C->>S: Places an order
    S->>H: POST /api/checkout/url/ (amount, redirect and callback URLs)
    H-->>S: checkout_url
    S-->>C: Redirect to checkout_url
    C->>H: Pays (card, Apple/Google Pay)
    H-->>S: Returns the customer to response_url
    H->>S: Postback to server_callback_url
    S->>H: POST /api/status/order_id/
    S-->>C: Order result page
    Note over S,H: The postback body is never trusted as state.<br/>The s2s status call is the single source of truth.
Loading

Authorize creates a preauth payment: the money is locked instead of charged until the store settles or releases it:

sequenceDiagram
    autonumber
    actor C as Customer
    participant S as Store
    participant H as Hutko

    C->>H: Confirms the payment
    H->>S: order_status approved (preauth) — authorized
    Note over S: Goods shipped, or the order is dropped
    S->>H: DoCapture — /api/capture, or Cancel — /api/reverse
    H-->>S: approved (captured) or reversed
Loading

While an order is still payable (created / processing) the capture flow does not create a new payment: the returning customer is redirected back to the same Hutko payment page (checkout_url) until the order is paid or expires.

Status mapping

Order status Payum status
created, processing pending
approved with an unsettled preauth authorized — the funds are locked, the store settles via DoCapture
approved captured
reversed refunded — covers both a released hold and a refunded payment; the host application distinguishes them by the previous transaction state
declined failed
expired expired

Hutko has no separate order status for a settled hold — both a successful preauth and its capture report approved. The gateway keeps the stage in the payment details (preauth, captured) and resolves the ambiguity locally.

Installation

composer require sol-parts/payum-hutko

Usage with PayumBuilder

use Payum\Core\GatewayFactoryInterface;
use Payum\Core\PayumBuilder;
use SolParts\PayumHutko\HutkoGatewayFactory;

$payum = (new PayumBuilder())
    ->addGatewayFactory('hutko', static function (array $config, GatewayFactoryInterface $coreGatewayFactory) {
        return new HutkoGatewayFactory($config, $coreGatewayFactory);
    })
    ->addGateway('hutko', [
        'factory' => 'hutko',
        'merchant_id' => 'your-merchant-id',
        'password' => 'your-merchant-password',
    ])
    ->getPayum();

Usage with Symfony PayumBundle

Register the gateway factory:

# config/services.yaml
services:
    app.hutko_gateway_factory:
        class: Payum\Core\Bridge\Symfony\Builder\GatewayFactoryBuilder
        arguments: [SolParts\PayumHutko\HutkoGatewayFactory]
        tags:
            - { name: payum.gateway_factory_builder, factory: hutko }

Configure a gateway:

# config/packages/payum.yaml
payum:
    gateways:
        hutko:
            factory: hutko
            merchant_id: '%env(HUTKO_MERCHANT_ID)%'
            password: '%env(HUTKO_PASSWORD)%'

Options

Option Required Description
merchant_id yes Merchant identifier issued by Hutko.
password yes Merchant password. Every request is signed with SHA1 over the pipe-joined non-empty request values, sorted by key, with the password prepended.

There is no sandbox option: Hutko serves live and test traffic on the same endpoint, and testing is done under the dedicated test merchant (1700002 / password test in the official documentation).

Payment details

ConvertPaymentAction fills amountin minor units (kopecks), as the Fondy protocol expects — along with currency, order_desc and order_id taken from the payment.

The gateway attaches response_url to the long-lived after-URL token and server_callback_url to a notify token, so both survive repeated visits.

Caveats

  • All amounts are in minor units (kopecks), including the partial amount of DoCapture and the reverse calls.
  • approved is ambiguous: Hutko reports it both for a successful preauth hold and for its settlement. The gateway tracks the stage with the preauth and captured payment details — do not strip them when manipulating the details externally.
  • /api/reverse/order_id/ is a single endpoint for both reversal of a hold and refund of a captured payment. The gateway always reverses the full transaction amount; partial refunds are not implemented.
  • The postback body is deliberately not parsed and its signature is not verified: the state is always re-fetched via /api/status/order_id/ under the merchant credentials. Answer anything but HTTP 200 and Hutko keeps redelivering the postback.
  • A checkout/url call answered with HTTP 200 but a non-success response_status is reported as an exception carrying the gateway error code and message together with the endpoint URL.
  • Error responses are reported with the HTTP status code and a truncated body snippet — balancer errors (429, 5xx) answer with HTML, not JSON.

License

Released under the MIT License.