# Technical documentation — sandwichbaronstore.co.za

Evidence date: 2026-10-04. Source: `master` at `c19a5e7` unless a section says `dev` or `qa`.

Secrets (database passwords, gateway keys, AWS keys) are present in the source and in the pipeline variable names. This document names the files and the host keys. It does not repeat secret values.

## Architecture

Server-rendered PHP on Apache, framework **Nemo** (`_framework/_nemo*.php`). Pages construct a `Nemo`, `NemoBasic`, or `NemoDetails` object and call `Display()` with a layout include.

Two MySQL databases per environment, both reached with `mysqli`:

| Connection | File | Used for |
|---|---|---|
| Customer | `system.php` | Storefront, `API/api.php` |
| Web admin and customer | `webadmin/system.php` | Admin UI and import |

There is no Composer manifest for the application, no Docker file, and no PHPUnit (or other) suite for the product. Third-party browser libraries under `webadmin/vendors/` include their own tests. Those are not tests of Sandwich Baron.

PHP version: **Unknown / Requires Confirmation**. The deploy hook installs `httpd` with `yum`, which indicates Amazon Linux or a RHEL-family host.

## Repository layout

| Path | Role |
|---|---|
| `index.php`, `client.*.php`, `login.php`, `resetPassword.php` | Customer entry points |
| `includes/` | Page classes (menu, order confirm, profile, register, email, user, settings) |
| `ajaxfunctions.php` | AJAX behaviours: basket, place order, XML, payment fields, registration |
| `project.functions.inc.php` | Rules, mail, audit, status lookup |
| `layouts/` | Header and page chrome |
| `views/` | Customer CSS and images. Contains a `.svn` directory |
| `payment/` | NuPay gateway classes and payment type/status wrappers |
| `API/` | Branch poll (`api.php`), test order (`testorder.php`), unused-looking WSDL |
| `webadmin/` | Admin application, its own `_framework` usage, crons, reports |
| `_framework/` | Nemo |
| `dompdf/`, `mpdf/`, `html2pdf/`, `fpdi/`, `PHPExcel/` | Vendored, also copied under `webadmin/` for some of them |
| `scripts/` | CodeDeploy hooks |
| `appspec.yml`, `bitbucket-pipelines.yml`, `codedeploy_deploy.py` | Deploy |
| `emailtemplates/` | Static HTML mail samples |
| `DB Update Scripts/` | Present. No `.sql` files found inside it |

`.gitignore` ignores `SESSION`, `images`, `menuimages`, `profilepictures`, and several `webadmin` data directories (`SESSION`, `Backup`, `images`, `importARCHIVE`, `importXML`, `importZIP`). Those directories are runtime data, not source.

On a case-insensitive filesystem, Git reports a collision between `images/EmailHeader.png` and `images/emailheader.png`. Only one of the two is in the working tree. Provenance: clone warning, 2026-10-04.

Many `*Copy*` and `*.bkp.php` files are leftover duplicates. They are not documented as the active path.

## Request flow

1. Apache serves a PHP file.
2. The page includes Nemo, which includes `system.php`.
3. `system.php` picks `$DATABASE_SETTINGS` by `$_SERVER['SERVER_NAME']`, forces the apex host to `www`, starts a PHP session in the configured session directory, connects MySQL, and loads every `sysSettings` row into `$SystemSettings`.
4. Customer pages keep the basket and the user on `$_SESSION`.
5. Checkout calls functions in `ajaxfunctions.php` (`ajaxPlaceOrder`, `generateXML`, `getPaymentGateway`, `getEFTPaymentGateway`).
6. The branch later calls `API/api.php`.

`index.php` sends `Location: client.menu.php` and then still calls `$page->Display()`. Provenance: **Implemented**.

## Environments

Active host keys in `system.php` / `webadmin/system.php` on `master`:

| Host key | Customer database | Web-admin database | Notes in source |
|---|---|---|---|
| `debian`, `debian.net` | `sandwich_baron_customer` | `sandwich_baron_webadmin` | Local. Hostname `debian` |
| `lamp` | `sandwich_baron_customer` | `sandwich_baron_webadmin` | Localhost |
| `dev.overdrive.co.za` | `sand_baron_customer` | No host key in `webadmin/system.php` | Host `sql19.jnb1.host-h.net`. Customer file only |
| `beta.sandwichbaronstore.co.za` | `beta_customer` | `beta_webadmin` | RDS in `eu-west-1`. HTTPS forced |
| `www.sandwichbaronstore.co.za` | `sbolocustomer` | `sbolowebadmin` | Same RDS host as beta, different database names. HTTPS forced. Empty `SERVER_NAME` is rewritten to this host |

A commented block still contains an older localhost definition for the www host. It is not the active assignment.

Beta and www share one RDS hostname and one database user in the source, with different database names. Provenance: **Implemented** as configuration. Whether that user is still valid is **Unknown / Requires Confirmation**.

`display_errors` is `1`. `error_reporting` hides strict, notice, and warning. A failed MySQL connect prints the settings object (`print_rr`), which includes the password. Provenance: **Implemented** in `system.php`.

## Deployment

`appspec.yml` (CodeDeploy):

- Linux.
- Copy the repository to `/var/www/html/sandwichbaronstore/`.
- Owner and group `sbftp`.
- `BeforeInstall`: `scripts/install_dependencies` (`yum install -y httpd`), 300 seconds, root.
- `ApplicationStop`: `scripts/remove_archives.sh` deletes old CodeDeploy revision directories except the last successful install.
- `ApplicationStart`: `scripts/start_server` (`service httpd start`).

`scripts/stop_server` exists and stops `httpd` if `pgrep` finds it. The appspec does not call it. `ApplicationStop` runs the archive cleaner instead. Provenance: **Implemented**.

`bitbucket-pipelines.yml`:

- Image `python:3.5.1`.
- Branch `qa` exports `QA_AWS_ACCESS_KEY_ID`, `QA_AWS_SECRET_ACCESS_KEY`, `QA_AWS_DEFAULT_REGION`, `QA_APPLICATION_NAME`, `QA_DEPLOYMENT_GROUP_NAME`, `QA_S3_BUCKET`, installs `zip` and `boto3==1.3.0`, zips the tree to `/tmp/artifact.zip`, runs `python codedeploy_deploy.py`.
- Branch `prod` does the same with `PROD_*` variables.

Remote branches that exist: `master`, `dev`, `qa`, and the `dev-*` feature tips listed in the project plan. There is **no** `prod` branch. Pushes to `master` or `dev` do not match this pipeline file.

`qa` is the pipeline branch that exists. Its tip is 2020-09-17, which is behind `dev` (2021-08-24).

Which CodeDeploy deployment group tracks which branch is **Unknown / Requires Confirmation**. The AWS resource names are pipeline variables, not committed.

## Data

The database is not in Git. Tables named by the active SQL include:

| Area | Tables |
|---|---|
| Identity | `tUser`, view `sysUser`, `tAddress`, `tTelephoneNumber`, `tAuthUser`, `OLOowner` |
| Geography | `OLOcountry`, `oloProvince`, `oloSuburb`, `OLOobject` (deleted flag) |
| Branch | `OLObranch`, `OLObranchHours`, `OLObranchNuPay`, `branchFastPay`, `publicHoliday` |
| Menu | `oloMenuSheet*`, `tOrderableItem` and related message-group tables, `OLOhappyHour` |
| Order | `tOrder`, `tOrderedItemHolder`, `tOrderItem`, `tOrderXML`, `tOrderXMLAudit`, `tOrderPayment`, `tPaymentLog`, `tOrderStatus`, `tAudit`, `tOrderAudit` |
| Rules | `oloRuleProvider` (timeouts, minimum order, email-from, delivery-charge id, sales groups), `oloDriverTip`, `sysSettings` |

`OLOobject.deleted` and `deletionPending` hide suburbs and happy-hour rows.

Order XML shape (`generateXML`):

```text
XOrderList
  Batch = 0
  CompanyNumber = site order id
  DepartmentList empty
  Order
    SalesModeId (4 delivery, 5 collect)
    OrderItems / OrderItemElements (PLU, price, sequence)
    DeliveryTime (only for a future order)
    PaymentDetails (isPaid, PaymentType, PaymentPrice, DriverTip rands)
    Debtor (cell as AccountNumber, name, address, email, notes, DeliveryCharge PLU)
```

Debug copies of that XML are written under `/var/www/html/sandwichbaronstore/SESSION/` as `bxml{order}.txt` and `axml{order}.txt`. Provenance: **Implemented**.

## API

`API/api.php` query parameters: `branchid`, `user`, `password`, `xml`.

Response when authenticated: HTTP content type `application/xml`, body `<xml>` plus the order document plus the day's status list. The status-line helper references `$page`, which is not constructed in the active script. Provenance: **Implemented** as written. Whether the status line always emits is **Unknown / Requires Confirmation**.

`API/testorder.php` uses the same credentials, inserts a negative `pkiOrderId`, and uses user id `27.3` (a non-integer literal in the SQL). Check-in update is commented out.

`API/wsdl/` is not referenced by `api.php`.

## Payments

| Piece | Detail |
|---|---|
| `payment/NuPayGateway.php` | Builds a NuPay payload. Access key is `md5(accessKey + Y/m/d + clientReference2)`. Author comment: Petrus Rademeyer, 30 March 2010 |
| `payment/NuPayTestGateway.php` | Present. Not used by `getPaymentGateway` |
| `getPaymentGateway` | Branch row from `OLObranchNuPay`. Amount in cents. Client reference `SB-{orderId}-{uniqid}` stored on `tOrder.sClientReference`. Merchant id `10000` uses `clientReference`; otherwise `merchantUsername` |
| `getEFTPaymentGateway` | `branchFastPay`. Item name `Sandwich Baron Order`. Amount in rand |
| Return URLs | `sysSettings` keys `PaymentGatewaySuccessURL`, `PaymentGatewayFailedURL`, `EFTPaymentGatewaySuccessURL`, `EFTPaymentGatewayFailedURL`, `EFTPaymentGatewayNotifyURL` |

Card success page: `paymentSuccess.php`. EFT server notify: `paymentEFTNotify.php`.

## Background work

These are PHP URLs, not queue workers. Nothing in the repository schedules them.

| Script | Behaviour |
|---|---|
| `webadmin/cron.daily.importXML.php` | Import a KingPOS ZIP from `FileLocationImport` unless maintenance is on or `ImportedZIP` is 0 |
| `webadmin/cron.daily.importStagingOLO.php` | Copy staging OLO rows from the web-admin database into the customer database |
| `webadmin/cron.daily.importImport.php`, `importComplete.php`, `importMaintenance.php` | Sibling cron files. Headers do not describe the step. **Unknown / Requires Confirmation** beyond "they are part of the import set" |
| `cron.daily.orderTimeoutCustomer.php` | Fail stale Queued and InProcess orders. Delete old sessions |
| `webadmin/cron.daily.orderTimeout.php` | Redirects to the customer timeout script |
| `webadmin/cron.daily.orderTimeoutWebadmin.php` | Separate web-admin timeout file. Not read in full |

`generateXML` and the timeout script use filesystem paths under the CodeDeploy directory and `SESSION`.

## Auth and sessions

- Session save path is per host key (under `DOCUMENT_ROOT`, typically `SESSION` or a project subdirectory).
- `session.gc_maxlifetime` = 7200.
- Customer idle limit = `consumerSessionTimeout` minutes.
- Password framework: `_framework/_nemo.password.cls.php`.
- Owner redirect puts MD5 material on the web-admin query string.
- API passwords are query parameters and are written into `tOrderXMLAudit` on failure.

## Security observations

These are facts about the tree, not a pentest.

- Database credentials are hardcoded in `system.php` and `webadmin/system.php`, including the beta and www RDS entries. A failed connection prints them.
- `phpinfo.php` is in the web root.
- `display_errors` is on.
- API and owner login move passwords in the query string.
- SQL is built by concatenating request values (`branchid`, order numbers, status XML text). `simplexml` parses the POS body.
- `ajaxPlaceOrder` interpolates prices, ids, and the delivery date into `INSERT` statements.
- Session and XML debug files are written on disk under `SESSION`.
- The pipeline image is Python 3.5.1 and pins `boto3==1.3.0` (2016-era).
- `.gitignore` does not ignore `system.php`.

No rate limit, CAPTCHA, or row-level security layer was found. **Unknown / Requires Confirmation** whether Apache or a firewall adds any of that in front of the host.

## Front end

Customer UI is table layout plus page-local CSS (`views/css`) and jQuery-style AJAX from `ajaxfunctions.php`. Layouts: `layouts/layout.basic.incl.php`, `layout.menu.incl.php`, `header.incl.php`.

Web admin uses a Bootstrap-style grid (`col-md-12`) and the vendored theme under `webadmin/vendors/`.

Google Tag Manager is injected in `layouts/header.incl.php` on `dev` and `qa` only.

## Development and testing process

- Git hosts on Bitbucket, workspace `overdrive`, slug `sandwichbaronstore.co.za`.
- Authors on the history inspected: `heinrichoverdrive`.
- No pull-request template, no README, no CI test step. The only pipeline steps zip the tree and call CodeDeploy.
- Local hostnames `debian` and `lamp` are the documented developer targets inside `system.php`.
- `emailtest.php` sends template tests. It is not an automated test.

## Monitoring

No application log framework, metrics exporter, or health check was found. Audit rows go to `tAudit` / `tOrderAudit` / `tOrderXMLAudit`. Import uses `importLogger` in the cron files. Branch liveness is `dtLastCheckin`, surfaced to customers as the offline page.

**Unknown / Requires Confirmation:** anything that watches the host outside this repository (CloudWatch, the CodeDeploy console, mail bounce handling).
