> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useduro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Joomla / VirtueMart

> The VirtueMart 3.x / 4.x payment plugin for Nomba hosted checkout on Joomla, with signed + API-reverified notifications and refund on order-status change.

`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

```
nomba.php                                     plgVmPaymentNomba, extends vmPSPlugin
nomba.xml                                     plugin manifest, files list, config fields
language/en-GB/en-GB.plg_vmpayment_nomba.ini  admin form labels and plugin strings
nomba/helpers/NombaClient.php                 token issue (cached) + checkout/verify/refund
nomba/helpers/NombaSignature.php              nine-field HMAC webhook signature check
nomba/tmpl/response.php                       return-page confirmation template
index.html                                    directory listing guard
```

## 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:

   ```
   index.php?option=com_virtuemart&view=pluginresponse&task=pluginNotification&pm=<methodId>
   ```

   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](/integrations/architecture) 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):

```bash theme={null}
docker run --rm -v "$PWD":/repo -w /repo/tools/tests composer:2.7 composer install
docker run --rm -v "$PWD":/repo -w /repo/tools/tests php:8.3-cli vendor/bin/phpunit -c phpunit-joomla.xml
```

`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.
