Skip to main content
integrations/joomla/ is a VirtueMart payment plugin, plgVmPaymentNomba (group="vmpayment", element nomba), that adds Nomba hosted checkout to a VirtueMart storefront on Joomla. Shoppers are redirected to Nomba to pay; VirtueMart is told the order is confirmed once the return page and the signed webhook both agree the transaction is paid. Full refunds run from VirtueMart’s own order-status change, with no separate admin screen. A parallel Duro plugin lives alongside it. Source: joomla/README.md, joomla/RUNTIME_TEST.md.

Platform support

Built against the classic non-namespaced VirtueMart plugin API (vmPSPlugin), the same API every real shipped VirtueMart payment plugin uses on VirtueMart 3.x and 4.x. Runs on Joomla 3.10, Joomla 4, and Joomla 5, matching whichever Joomla versions the installed VirtueMart release supports. The plugin uses no version-specific API beyond vmPSPlugin, vRequest, JFactory, and JURI, which are stable across that range.

Structure

Install

Build with bash tools/build.sh (produces dist/nomba-vmpayment.zip with nomba.php and nomba.xml at the zip root, as VirtueMart’s installer requires, alongside language/ and nomba/).
  1. Upload the zip in the Joomla admin under System, Install, Extensions (Upload Package File).
  2. Enable the plugin: Plugins, filter by the vmpayment group, enable VmPayment - Nomba.
  3. In VirtueMart’s Store, Payment Methods, add a new payment method with Payment Plugin set to nomba, then fill in:
    • Account ID: the Nomba parent (business) account ID. VirtueMart sends this in every API call’s accountId header; a sub-account ID is rejected by Nomba with HTTP 403.
    • Sub-account ID (optional): if set, payments route into that sub-account via order.accountId on the checkout request.
    • Client ID / Client secret: from the Nomba dashboard API Keys page.
    • Signature key: from the dashboard’s webhook settings, used to verify the nomba-signature header.
    • Test mode: on by default, targets https://sandbox.nomba.com; off targets https://api.nomba.com. Both hosts use the same /v1/checkout prefix.
    • The four order-status fields (pending, success, refunded, canceled) and the accepted currency, standard VirtueMart payment-method fields.
  4. In the Nomba dashboard, register the webhook URL for this store:
    where <methodId> is the virtuemart_paymentmethod_id of the method created in step 3. The plugin computes and sends its own return URL automatically as part of the checkout request, so nothing else needs registering.

Lifecycle

The plugin implements the shared verification-first lifecycle across VirtueMart’s payment triggers:
  1. Confirm (plgVmConfirmedOrder): once VirtueMart creates the order (Pending), the plugin builds a vm-<orderId>-<random> order reference, calls POST {prefix}/checkout/order, stores a pending row keyed by that reference in the plugin’s own internal table (#__virtuemart_payment_plg_nomba), and returns VirtueMart’s redirect response so the shopper is sent to data.checkoutLink.
  2. Redirect: the shopper pays on Nomba’s hosted page.
  3. Return verify (plgVmOnPaymentResponseReceived): if the stored row is still pending, the plugin calls GET {prefix}/transaction and finalizes the order only when the API reports it paid with a matching amount; a mismatch or unpaid result leaves the order pending for the webhook. The return page never grants value on its own say-so.
  4. Webhook API re-verify (plgVmOnPaymentNotification): Nomba POSTs a signed payment_success. The plugin checks the nine-field HMAC-SHA256 signature (nomba-signature header) against the nomba-timestamp header and a freshness window, rejecting with 401 on any failure. Only then does it call the verify endpoint again and compare the verified amount to the stored order total; a match finalizes the order (VirtueMart status set to the method’s configured success status), a mismatch holds it at the configured pending status. The webhook payload itself is never trusted for the amount.
  5. Refund on status change (plgVmOnUpdateOrderPayment): when an admin changes a paid order’s status to the method’s configured “Refunded” status, the plugin calls POST {prefix}/refund with the stored Nomba transaction id. There is no separate refund screen; the trigger is VirtueMart’s native order-status workflow.

Verification

VirtueMart has no CLI installer and no lightweight way to stand up a storefront in this environment (unlike WordPress/WP-CLI or the PrestaShop Docker image), so the plugin is verified statically rather than with a scripted end-to-end demo:
  • Unit tests (18) cover the two pure, framework-free classes, NombaClient and NombaSignature, against a stubbed HTTP layer: token caching, checkout order creation, verify normalization, refund acceptance (including the no-data-object code: "00" shape), and the nine-field signature computation plus timestamp freshness.
  • The plugin class (nomba.php, which needs a live vmPSPlugin, VirtueMartModelOrders, VmModel, and database) is verified by php -l and a line-by-line static review against .superpowers/sdd/virtuemart-api-reference.md, itself built from real VirtueMart source: every plgVm* trigger name, the vmPSPlugin constructor sequence, getVmPluginMethod/selectedThisElement self-selection, processConfirmedOrderPaymentResponse return codes, storePSPluginInternalData, and the order-status field names all match the reference.
  • The manifest (nomba.xml) is checked for well-formed XML, and the packaged zip layout is checked against the reference’s packaging list.
Run the unit suites (Joomla runs isolated because its classes share global names with PrestaShop’s):
RUNTIME_TEST.md in the plugin folder is the full step-by-step runtime checklist (install, configure, checkout redirect, webhook, refund) with the exact field names and status labels, including how to point the plugin at the local tools/mock-nomba/ server via the NOMBA_API_BASE constant for a credential-free loop.