valiton/datatrans-bundle
Valiton Datatrans Bundle is a Symfony integration for the Datatrans payment gateway. It wires Datatrans services into your application and helps configure and handle payment transactions and callbacks with less boilerplate.
This bundle is a thin Symfony 3 bridge between the legacy Datatrans UPP/XML API and JMSCorePaymentBundle. There are only 3 stars, 0 dependents, and a single late-2022 release, so treat it as a reference implementation to vendor or fork, not a maintained dependency.
Minimal adoption path:
composer.json and check PHP and Symfony version ranges plus the pinned JMS payment bundle version. If you are on Symfony 4+, this will not install without forking. Confirm the JMS PaymentCoreBundle version it expects, because the bundle is useless without it.composer require valiton/datatrans-bundle will pull JMS along with it if constraints allow. Register both in app/AppKernel.php; order matters only in that JMS should be enabled before Datatrans so its services and Doctrine mappings exist.DependencyInjection/Configuration.php — the authoritative config tree (merchant_id, sign_key, sandbox/test, and whatever endpoint/URL options exist).Resources/config/services.yml — the service IDs you will actually inject. The class names vary between releases, so do not assume; copy IDs from here.config.yml under the bundle's root key (check Configuration.php for the exact node name), using parameters so test/prod keys are environment-driven:# config.yml — verify node/key names against Configuration.php
vaulton_datatrans:
merchant_id: "%datatrans_merchant_id%"
sign_key: "%datatrans_sign_key%"
sandbox: "%kernel.debug%"
PaymentInstruction + PaymentTransaction via JMS, persist them with your own unique refno, then redirect the user to the Datatrans form with merchantId, amount (in minor units), currency, refno, successUrl, errorUrl, cancelUrl, and the signature. A simple "pay once, capture immediately" flow (purchase) is the fastest end-to-end validation that config, signing, and return URLs all work.Do not start with refunds or voiding — validate authorize + return handling first, because the return/callback format is where the legacy API bites hardest.
Pattern 1 — JMS-driven instruction flow (the intended path).
The bundle assumes you already model payments as JMS PaymentInstruction/PaymentTransaction entities. Build the instruction, set amount in minor units, attach a transaction, flush, then hand the instruction to the Datatrans service to obtain the redirect/form payload. Keep the instruction ID as your internal correlation key and store the Datatrans refno/uppTransactionId on the transaction's extended data.
Pattern 2 — Hosted page + return controller. Typical flow:
// Controller: start
$instruction = $this->get('jms_payment_core.payment_instruction_factory')->create();
// ... set amount, currency, your refno ...
$em->persist($instruction);
$em->flush();
$response = $this->get('datatrans.payment_service')->purchase($instruction);
return $this->redirect($response->getRedirectUrl()); // or render a self-posting form
// Controller: success/error/cancel endpoint
public function successAction(Request $request)
{
// Log the raw payload first — the legacy API returns inconsistent fields.
$this->get('logger')->info('datatrans.return', $request->request->all());
$refno = $request->request->get('refno');
$status = $request->request->get('status'); // do NOT trust for fulfilment
// Verify server-side via the service/getStatus before marking paid.
}
Pattern 3 — Server-to-server authorize/settle/void.
Wrap the bundle's client service in your own DatatransGateway interface with authorize, capture, refund, void, status. This is the single highest-value thing you can do: it isolates every legacy quirk behind one seam and makes a future migration to Datatrans' JSON API v2 a swap of one class rather than a hunt through controllers.
Pattern 4 — Reconciliation job.
A nightly console command that iterates transactions stuck in pending, calls getStatus by refno/transaction id, and settles or marks them failed. Because the browser return is unreliable (users close tabs, providers retry), server-side polling is how you keep state correct.
Integration tips:
refno a stable, unique, human-readable order reference; Datatrans ties everything to it.successUrl/errorUrl/cancelUrl including scheme and host; relative URLs silently fail in some Datatrans flows.PaymentAuthorized, PaymentCaptured) from the gateway, not from the bundle, so the rest of the app never depends on Datatrans types.PaymentCoreBundle is itself lightly maintained, and 0 dependents means nobody is filing the bugs you will hit. Budget for forking into src/ (composer.json "replace") and owning the code.upStart, upStart.jsp-style flows and XML status calls). New Datatrans merchants are onboarded onto the JSON API v2 with HMAC signing and a different request shape. Confirm with Datatrans support which onboarding you received before writing a line of code.1250; zero-decimal currencies (JPY) are not. Currency must match the merchant's configured currencies or Datatrans rejects the request with an unhelpful error.status/success query parameters in the return URL are forgeable. Always confirm via a server-side status call or the settlement/reconciliation feed before fulfilling an order.StreamHandler) to capture the exact outbound form/XML and inbound return. Datatrans support will ask for the refno and uppTransactionId — log both on every request.decorates: in services.yml) rather than subclassing; override endpoint/host parameters via the config tree if present, otherwise a compiler pass that mutates the service definition after the bundle load.How can I help you explore Laravel packages today?