mews/pos-bundle
mews/pos-bundle is a PHP package for integrating Mews POS into your application. It provides bundle configuration and service wiring to connect point-of-sale workflows, sync POS data, and support hospitality property integrations with less boilerplate. Suitable for Symfony projects needing Mews POS...
This is mews/pos v2 — the Turkish virtual-POS abstraction layer (Asseco/Est, PosNet, KuveytTürk, VakıfKatılım, PayFlex, Iyzico, PayTR, Param). v2.0.0 is a rebuild-level major: namespaces moved, gateways were renamed, and the config schema was restructured. If you are onboarding fresh, you are on the good path — there is no legacy baggage to unlearn. If you are upgrading, read docs/UPGRADE-2.0.md before touching code, then follow the order below.
Prerequisites
>= 8.0 (7.4 is no longer supported)>= 5.4mews/pos ^2.0Minimal steps
composer require mews/pos:^2.0
php artisan vendor:publish --tag=pos-config
php -v # must be 8.0+
composer show mews/pos # must be 2.x, not 1.x
credentials.enc_key → credentials.secret_keytest_mode → gateway_configs.test_modelang → gateway_configs.langcredentials.payment_model and gateway_endpoints.payment_api_2Mews\Pos\Gateways\* → Mews\Pos\Gateway\*, and rename EstPos/EstV3Pos → AssecoPos, PosNet → PosNetPos.First use case: a single Asseco 3D Secure payment in test mode. This exercises the whole surface — account model, gateway config, 3D form generation, callback handling — and will surface every config mistake at once instead of one at a time.
use Mews\Pos\Gateway\AssecoPos;
use Mews\Pos\Model\Account\AssecoPosAccount; // exact class names: see docs/UPGRADE-2.0.md
$account = new AssecoPosAccount([
'bank' => 'akbank',
'merchant_id'=> config('pos.banks.akbank.credentials.merchant_id'),
'username' => config('pos.banks.akbank.credentials.username'),
'password' => config('pos.banks.akbank.credentials.password'),
'secret_key' => config('pos.banks.akbank.credentials.secret_key'), // was enc_key
'lang' => 'tr',
]);
Where to look first: docs/UPGRADE-2.0.md for the mapping table, then the gateway_configs block in the published config — it is the single source of truth in v2.
Treat gateway_configs as the new centre of gravity. v2 pushed per-gateway behaviour out of the bank block and into gateway_configs.{bank}.{gateway}. lang and test_mode now live there and are honoured by all gateways, which is a genuine improvement — you can flip a single bank to test mode without touching its credentials. Model this in your own app config as a merged array rather than duplicating bank blocks:
// config/pos.php
'gateway_configs' => [
'akbank' => [
'test_mode' => env('POS_TEST_MODE', true),
'lang' => 'tr',
],
],
Wrap gateway construction in a resolver, not in controllers. Gateway class names changed in this release and will change again. One binding, one place to fix:
// app/Services/Payment/GatewayResolver.php
public function make(string $bank): PosInterface
{
$config = config("pos.banks.{$bank}");
$account = $this->accountFactory($bank, $config); // your own factory
return PosFactory::create($config, $account, $this->gatewayClass($bank));
}
Note the package's own bundle AccountFactory was deleted in v2 — if you relied on it, this resolver is the replacement. Keep the account-construction switch here too.
Persist payment attempts before redirecting to 3D. The classic pattern: create a local Payment row with a generated order_id and status pending, call prepare() + get the 3D form data, render the auto-submitting form. On callback, look the row up by order_id, call the gateway's status/provision query, and only then mark it paid. Never trust the callback's success flag alone — always confirm server-side.
Choose the right 3D variant per gateway, not globally. v2 removed payment_api_2 and split Param 3D Host payments into a dedicated Param3DHostPos gateway. The rule of thumb:
Param3DHostPos, distinct account and config.Use a strategy layer for the new gateways. IyzicoPos and PayTrPos have materially different request/response semantics from the bank gateways (wallet-style redirects, different callback payloads). Rather than if ($bank === 'iyzico') sprinkled through controllers, define a small PaymentGatewayDriver interface in your app with initiate(), handleCallback(), refund() and let each package gateway sit behind one implementation. This is what makes swapping a bank a one-file change.
Test mode belongs in gateway_configs, and test it explicitly. Write a feature test that asserts the generated 3D form action URL points at the sandbox host — that single assertion catches almost every test_mode misconfiguration, including the v1 leftover of putting test_mode at bank level where v2 now ignores it.
Integration tip — queue the reconciliation. Bank status endpoints are slow and occasionally flaky. On callback, respond 200 fast, dispatch a job that queries the gateway and updates the order. Use retry() with backoff and an idempotency guard on order_id, because banks retry callbacks.
The silent failures are the config ones. payment_model, bank-level test_mode, and bank-level lang were removed, not deprecated. A leftover test_mode => false at bank level will not raise an error — it will just be ignored, and your sandbox traffic hits production. Grep your config for enc_key, payment_model, test_mode, and payment_api_2 and confirm zero hits outside gateway_configs.
enc_key → secret_key is the sneakiest rename. It is inside credentials, so a config('pos.banks.x.credentials.enc_key') call returns null and the gateway fails with a hash/signature error that looks like a bank-side problem. If you see "invalid hash" or "MAC verification failed" after upgrading, check this key first.
Namespace moves break string-based resolution. Mews\Pos\Gateways\* → Mews\Pos\Gateway\* (singular). If you resolve gateway classes via config strings, class_exists checks, or a DI container alias, those strings need updating too — PHP will not warn you until instantiation. Same for PayForAccount → PayForPosAccount (now under Mews\Pos\Model\Account, not Entity\Account).
gateway_3d is now optional for KuveytPos / VakifKatilimPos / PayFlexCPV4Pos. v2 derives it from payment_api. Remove the explicit key or you will be maintaining a value that is ignored — and if you had a wrong value there, v1 hid it and v2 fixes it, which can change behaviour on upgrade. Re-run a 3D payment for those three banks specifically.
Test mode is per-gateway now, not per-bank. In multi-gateway setups (e.g. a bank with both a standard and a 3D-host gateway) you can end up half in sandbox. Assert the endpoint host in tests, or log the resolved endpoint at boot in non-production.
Debugging order when a payment fails: (1) dump the fully merged config after gateway_configs resolution; (2) confirm secret_key and merchant_id are populated, not null; (3) confirm test_mode resolves correctly; (4) only then log the raw gateway request/response. Banking APIs return generic error codes — the package's response object carries the bank's raw message, which is usually the only thing that tells you the truth.
Extension points: the package's own AccountFactory is gone, so subclassing/extending account creation is now entirely your code — that is the intended extension seam. For gateway behaviour tweaks, prefer wrapping the package gateway behind your own driver interface over subclassing the concrete Gateway\* classes; the class names already moved once in this release and are not a stable extension point.
Pin the version and read release notes before composer update. This release changed namespaces, config keys, and class names simultaneously. Add "mews/pos": "2.0.*" at minimum, and keep docs/UPGRADE-2.0.md linked from your README so the next major is a checklist rather than an archaeology project.
Framework version coupling: dropping Symfony 4 and PHP 7.4 is good hygiene but check your CI matrix and any Docker base images — a Laravel app still on PHP 7.4 cannot install this at all, and the composer error is a confusing dependency conflict rather than a clear version message.
How can I help you explore Laravel packages today?