Integrations
OpenCart 3

OpenCart 3

Verified public merchant ZIP 0.1.2: lunixi-opencart3-0.1.2.ocmod.zip (opens in a new tab) · SHA256SUMS (opens in a new tab) · Release v0.1.2 (opens in a new tab). The CI-built installation package was downloaded and verified on 2026-10-31. Use this asset rather than GitHub's automatic source archive.

Public CI ZIP SHA256: 715973a2a53405ef42442a7ea763a57e470f377af362c527b297bf81092d1b1f.

Lunixi Payments 0.1.2 integrates the original OpenCart 3.0.5.1 checkout and order lifecycle. The standalone merchant ZIP bundles a namespace-isolated PHP SDK 0.29.5. Merchants do not need Composer.

The existing Lunixi form opens on the merchant website by default; optional hosted checkout uses the validated backend paymentPageUrl. Enabled methods, eligible meal cards, shopping credit, installments, insurance and multi-card availability are managed in the Lunixi panel/form. The extension does not collect card numbers/CVV or add a second set of method switches.

Source, merchant ZIP build and examples (opens in a new tab) · English/Turkish repository documentation (opens in a new tab)

Requirements

  • OpenCart 3.0.5.1, original native order model; other versions require a new compatibility review.
  • 64-bit PHP 8.3+, within the platform's supported range; BCMath, cURL, JSON, Sodium and MySQLi.
  • MySQL/MariaDB with all shop and extension tables InnoDB and HTTPS storefront/webhook.
  • Registered Lunixi merchant, Ed25519 key and webhook secret; separate backed-up 32-byte server encryption key.

OpenCart 3 defaults to MyISAM. A DBA must back up, test restoration, pause order writes and review/convert every applicable table before enabling payments. The module reports nontransactional tables and never silently changes production engines. Modified order models and unreviewed active order/history, total or fraud hooks fail preflight. Native audited business/fraud hooks remain active, including fraud fulfillment holds after capture. Third-party themes/extensions are not assumed compatible. Native recurring/subscription carts are unavailable in this version.

Install and configure

  1. Back up the store and test on staging. Complete the InnoDB/platform/hook preflight.
  2. Generate the independent key locally:
php -r 'echo base64_encode(random_bytes(32)), PHP_EOL;'

Set LUNIXI_OPENCART_ENCRYPTION_KEY in both PHP-FPM and worker environments outside the web root/database. Back it up separately; losing it makes retained encrypted credential profiles unreadable.

  1. Upload lunixi-opencart3-0.1.2.ocmod.zip in Extensions → Installer and install Lunixi Payments under Payments. The native upload/ layout does not replace core files.
  2. Grant access/modify permission to the Lunixi payment route. Capture/refund/void also require sale/order modify permission.
  3. Edit the extension and select the native store. Enter merchant ID, key ID, Ed25519 private key and webhook secret. Values are encrypted; secret fields remain blank on reload, and blank inputs preserve an existing profile. Configure stores separately.
  4. Keep the default embedded checkout or select optional hosted checkout, and configure optional preauthorization. The installer creates separate localized awaiting/authorized/review statuses. Map only paid into native processing/complete statuses; all other mappings must be outside those sets.
  5. Register the HTTPS webhook displayed in the screen, for example:
https://shop.example/index.php?route=extension/payment/lunixi/webhook

Preserve raw body bytes and all X-Lunixi-* headers through the proxy. Configure available methods and form settings in Lunixi.

  1. Schedule the worker, verify it on staging and then enable the method.

Scheduled worker and native admin operations

Run every minute for each store with the same server key and extensions as the web process:

php /absolute/shop/system/library/lunixi3/cli.php --root=/absolute/shop --store-id=0

The web cron route is forbidden. Native admin capture/refund/void requests create a durable queued operation ID; the catalog worker executes native order/stock code in its correct context. Refresh operation status after the worker runs. pending or uncertain is not success: retry/reconcile the same operation ID and exact amount. New operations are blocked while a previous one remains unresolved. Only authenticated settlement evidence confirms the requested capture, refund or void. Refunds cannot exceed captured funds; multi-card refunds use the shared aggregate API.

The worker reconciles missed webhooks and delivers native notifications after commit. Interrupted sending/uncertain mail rows require review before any explicit resend. Uninstall disables the method while preserving financial history, encrypted profiles, operation IDs, inbox/outbox and custom statuses.

Payment evidence and security

Browser query parameters and form events never mark paid. Tokens are returned only after session, customer, store and order ownership checks. Raw-body v2 HMAC, freshness and signed event identity are verified; an authenticated payment GET checks merchant/payment/order/currency and the immutable exact-money snapshot. Durable row locks, event IDs and native stock/history writes share one transaction. Concurrent/replayed deliveries apply once; a failed native write rolls back event consumption too.

Partial capture and incomplete multi-card aggregates remain on hold. A buyer-selected installment fee must be correlated to the persisted order fingerprint and is recorded separately from the basket. Changed native order totals/currency cannot be completed by an old attempt, including a rate-limited browser poll. Lost create responses recover only from a verified payment webhook and authenticated GET proving the original opaque reservation hash; bound IDs are never replaced. Native fraud/manual holds remain effective even for captured funds.

Only https://cdn.lunixi.com/lunixi-checkout.min.js renders the embedded form. Signing/webhook secrets remain server-side. For nonce-based CSP, a trusted server integration can set native config lunixi_csp_nonce for the current response; it is escaped on scripts/styles. The default gateway is https://api-gateway.lunixi.com; any alternative origin requires an exact operator-controlled HTTPS environment allowlist.

Keep old encrypted profiles and the independent server key through reconciliation/refund retention, and coordinate gateway key revocation. Avoid raw-body/token/credential logs. Security and operational guidance (opens in a new tab).

Verification and distribution

The native acceptance suite installs official OpenCart 3.0.5.1 with real models, events, users and MariaDB, then deploys the built ZIP. It tests exact decimal FX/ISO precision, MyISAM rejection, rollback, stock/history, fraud holds, signed tamper/replay/collision, concurrent processes, capture/refund/void, delayed confirmation, changed snapshots and lost responses. PHPStan level 5 checks native glue, alongside the shared SDK's independent tests. Administrator form, CSRF, permissions and uninstall retention are tested in a separate native process.

This release includes public source and the verified merchant ZIP. Offline acceptance uses synthetic gateway transport; no live charge or Marketplace approval is claimed. Reproduce tests/build (opens in a new tab). Extension license: GPL-3.0-or-later; bundled SDK: MIT.

Financial outcomes that remain uncertain are held for review. FAILED alone does not authorize a new attempt: the public payment read does not expose definitive provider outcome certainty. Unresolved financial retries stop after 23 hours; authenticated GET reconciliation remains available. A timeout, token expiry or browser callback never generates a fresh financial key.

Provider browser return

Lunixi hosted, split and 3DS flows return by POST to the generated returned route. This route alone accepts an informational cross-site POST without a session/CSRF token: it ignores the complete form body and immediately sends 303 to a relative same-origin GET of that route, keeping only the numeric order_id from the original callback URL. It does not fetch a payment, disclose order data or change financial state. The GET retains native session/customer/store/order ownership and authenticated payment reconciliation. Checkout/admin POST protections remain required; browser success fields never prove payment.

The application rejects declared callback bodies above 64 KiB. Configure the HTTPS reverse proxy to enforce the same wire-body limit on this route, including chunked requests; keep the signed webhook's separate 1 MiB limit. Do not convert the callback to a checkout/confirm POST or remove ownership checks from GET.

Checkout mode and existing attempts

Embedded checkout is the default; optional hosted checkout uses the exact validated paymentPageUrl returned by the backend. Both modes open the same Lunixi form and support the methods, multi-card, installments and insurance enabled for the merchant. None of these features requires redirecting. Never derive a payment-page URL from the browser token.

Existing explicit hosted settings are retained. Checkout mode, callback, preauthorization and locale are bound to an attempt's original request. Changing them cannot reset its request hash or idempotency key. If an older attempt has no stored backend payment-page URL, optional redirect fails closed: reconcile that attempt rather than constructing a URL, deleting history or creating a replacement key. Embedded rendering still uses the protected browser configuration. Keep the signed webhook, authenticated settlement checks and informational 3DS/split return routes.