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 withbash 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/).
- Upload the zip in the Joomla admin under System, Install, Extensions (Upload Package File).
-
Enable the plugin: Plugins, filter by the
vmpaymentgroup, enable VmPayment - Nomba. -
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
accountIdheader; 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.accountIdon 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-signatureheader. - Test mode: on by default, targets
https://sandbox.nomba.com; off targetshttps://api.nomba.com. Both hosts use the same/v1/checkoutprefix. - The four order-status fields (pending, success, refunded, canceled) and the accepted currency, standard VirtueMart payment-method fields.
- Account ID: the Nomba parent (business) account ID. VirtueMart sends this in every API call’s
-
In the Nomba dashboard, register the webhook URL for this store:
where
<methodId>is thevirtuemart_paymentmethod_idof 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:- Confirm (
plgVmConfirmedOrder): once VirtueMart creates the order (Pending), the plugin builds avm-<orderId>-<random>order reference, callsPOST {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 todata.checkoutLink. - Redirect: the shopper pays on Nomba’s hosted page.
- Return verify (
plgVmOnPaymentResponseReceived): if the stored row is still pending, the plugin callsGET {prefix}/transactionand 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. - Webhook API re-verify (
plgVmOnPaymentNotification): Nomba POSTs a signedpayment_success. The plugin checks the nine-field HMAC-SHA256 signature (nomba-signatureheader) against thenomba-timestampheader 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. - Refund on status change (
plgVmOnUpdateOrderPayment): when an admin changes a paid order’s status to the method’s configured “Refunded” status, the plugin callsPOST {prefix}/refundwith 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,
NombaClientandNombaSignature, against a stubbed HTTP layer: token caching, checkout order creation, verify normalization, refund acceptance (including the no-data-objectcode: "00"shape), and the nine-field signature computation plus timestamp freshness. - The plugin class (
nomba.php, which needs a livevmPSPlugin,VirtueMartModelOrders,VmModel, and database) is verified byphp -land a line-by-line static review against.superpowers/sdd/virtuemart-api-reference.md, itself built from real VirtueMart source: everyplgVm*trigger name, thevmPSPluginconstructor sequence,getVmPluginMethod/selectedThisElementself-selection,processConfirmedOrderPaymentResponsereturn 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.
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.