# Project plan — paymyfines-backend

Evidence date: 2026-10-08.

Source repository: `git@bitbucket.org:overdrive/paymyfines-backend.git`.

Inspected revision: `master` at `5b98fa7` (2022-02-15, Lloyd, "Merge branch 'master' of https://bitbucket.org/overdrive/paymyfines-backend"). Full hash `5b98fa7640e9cd4d1a71da5f18b107899179c4be`. That commit is `origin/HEAD`. There are no tags. History on `master` is 315 commits. The oldest commit date returned by `git log` is 2021-05-30.

The scan-host path named on the onboarding issue (`~/projects/sonar_scan/repos/paymyfines-backend`) was not on this machine. The tree below was read from the git revision above. That revision matches the commit the issue records (`5b98fa7`, 2022-02-15 12:44:26 +0200).

This plan describes what the repository contains. It does not decide what the product should become.

There was no prior Silicon Ark directory for `paymyfines-backend`. Related directories that already exist, and are separate repositories, are listed under Related knowledge.

## Product

**Documented in code and mail copy, not in a product brief.** Cognito custom-message subjects are "Pay My Fines: Sign Up", "Pay My Fines: Resent code", and "Pay My Fines: Forgot Passowrd" (`Handler.cs`, `GenerateCustomEmail`). The receipt subject is "PMF Online Payment Receipt". The from-address constant is `noreply@paymyfines.co.za` (`classes/Constants.cs`).

**Inferred from behaviour.** The API lets a caller look up traffic infringements from two upstream systems (iForce and eNatis), record an iVeri Lite card payment, email a PDF receipt, and later push the paid amount to the source system. It also stores user profiles, settings, API logs, finance rows, and dashboard graph definitions in MySQL.

`README.md` does not describe Pay My Fines. It is the Silicon Overdrive `serverlessDotNetStarter` guide (hello-world Lambda, .NET Core 3.1, example region `us-east-1`). A "Silicon sequence" at the bottom of that file deploys with AWS profile `pmf` to `https://4row8qyxh1.execute-api.us-east-1.amazonaws.com/dev/hello`. That guide disagrees with `serverless.yml`, which names service `tmt-pmf-prod`, stage `production`, region `eu-west-1`, and profile `pmf-prod`.

## Objectives (inferred from the implementation, not from a written plan)

- Expose HTTP Lambdas, in AWS `eu-west-1`, that search and pay infringements.
- Keep a local MySQL record of users, payments, and whether a notice was already paid here, so the caller can block a second payment.
- Settle successful iVeri payments with iForce and eNatis on a schedule, and retry rows already marked source-failed.
- Send Pay My Fines branded Cognito mail from templates stored in MySQL.
- Provide finance and graph data for an operator UI. This API does not check a role before returning that data.

## Scope

In scope, because it is committed:

- Serverless Framework service `tmt-pmf-prod`, stage `production`, runtime `dotnetcore3.1`.
- One Lambda class, `PMF.Handler` in `Handler.cs`, with one method per route.
- MySQL access through the Secrets Manager secret `pmf-prod-rds-6mUVsR` (full ARN is in `Services/SecretsManager.cs`).
- HTTP calls to the base URL hardcoded in `classes/Constants.cs` (`http://192.168.0.41:8080/api/`), used for both iForce and eNatis paths.
- iVeri Lite return handling (`POST /iveripayment`), receipt HTML, a PDF HTTP service (`http://10.106.3.158:8080/htmltopdf`), and S3 bucket `tmt-pmf-prod-invoices`.
- Amazon SES SMTP via secret `pmf-prod-ses-smtp-0oR1H2` and from-address `noreply@paymyfines.co.za`.
- A Cognito Custom Message trigger on existing user pool `tmt-pmf-prod-user-pool`.
- A scheduled settlement job and an unauthenticated HTTP trigger for the same job.
- A committed deployment zip `bin/release/netcoreapp3.1/pay-my-fines-lambda.zip` and committed `.serverless/` CloudFormation and state files.
- Assets `Assets/pmf_logo.svg` and `Assets/mystyle.css`. Receipt HTML in `PaymentReceiptService` loads a different logo URL from Amplify and does not reference those asset files.

Out of scope, because it is not in this repository:

- The public website, Cognito hosted UI, and iVeri hosted payment page.
- The iForce and eNatis systems themselves. Only the HTTP paths this code calls are visible.
- The MySQL schema (no migrations or DDL). Table and column names are visible only where SQL strings name them.
- Infrastructure as code for the VPC, RDS instance, Cognito pool, or secrets. `serverless.yml` only lists one security group and two subnet IDs to attach at deploy time.
- Automated deployment. There is no `bitbucket-pipelines.yml` in git. `sonar-project.properties` and `sonarqube_scan.bat` were named in the onboarding issue's scan-host listing and are not in this git tree.

## System architecture

```text
Caller (browser or other HTTP client)
  -> API Gateway REST API (CORS enabled, no authorizer in serverless.yml)
  -> .NET Core 3.1 Lambda (PMF.Handler, one method per route)
       -> MySQL (secret pmf-prod-rds)
       -> AWS Secrets Manager (eu-west-1)
       -> upstream base URL (hardcoded private IP)
            -> IForce/* and infringement/*
       -> Google reCAPTCHA siteverify
       -> SES SMTP (email-smtp.eu-west-1.amazonaws.com:587)
       -> PDF HTTP service (hardcoded private IP)
       -> S3 bucket tmt-pmf-prod-invoices (read of the PDF key the PDF service returns)

Cognito user pool tmt-pmf-prod-user-pool
  -> Custom Message trigger -> GenerateCustomEmail

EventBridge schedule rate(2 hours), name RunCronJobDev
  -> RunCronJob
  -> same upstream pay endpoints
  -> SES mail to a hardcoded address, CC users whose strRole is ["ADMIN"]
```

Package artifact expected by `serverless.yml`: `bin/release/netcoreapp3.1/pay-my-fines-lambda.zip`. `build.cmd` produces that path in Release. `build.sh` produces `bin/release/netcoreapp3.1/hello.zip` in Debug, which does not match the artifact path.

## Major components

| Area | Location | Role |
|---|---|---|
| HTTP and Cognito handlers | `Handler.cs` | All public Lambda entry points |
| Validation and string cleaning | `classes/Helper.cs`, `Model/APIModel.cs` | Required fields and DataAnnotations rules |
| Constants | `classes/Constants.cs` | Region, mail, upstream URLs, reCAPTCHA |
| DB and SMTP secrets | `Services/SecretsManager.cs` | Reads two secret ARNs at runtime |
| HTTP envelope | `Services/ResponseService.cs` | `{ result, payload }` plus CORS `*` |
| Local paid check | `Services/PaymentChecker.cs` | `infringement.intPaid = YES` blocks another payment |
| Source settlement | `Services/PaymentService.cs` | Posts paid notices to iForce and eNatis |
| Receipt | `Services/PaymentReceiptService.cs` | HTML, PDF service, S3 read, SES |
| Failed-settlement mail | `Services/MailService.cs` | Cron failure notice |
| Graphs | `Services/GraphService.cs` | Runs SQL stored in the `graph` table |
| Request and response models | `Model/` | iForce, eNatis, iVeri-related, graph, user |

## Milestones visible in git

No milestone document exists. The dates below are commit facts on `master`, not an approved plan.

| When | Evidence | What landed |
|---|---|---|
| 2021-05-30 | Oldest commit date on `master` | Repository history begins |
| Through 2022-02 | Commit subjects on `master` | iVeri transactions, receipts, PDF URL changes (public IP, private IP, load balancer, then load balancer removed), S3 PDF attach, receipt from-address, namespace cleanup |
| 2022-02-15 | `a9d9ded` / `ecb466f`, then merge `5b98fa7` | "allow 02 and other ID types" — last functional subjects before the merge that is HEAD |

`master` has not moved since 2022-02-15. Whether production still runs this revision is **Unknown / Requires Confirmation**.

## Implementation plan

There is no written implementation plan in the repository. The deploy path that is written down is:

1. `dotnet restore` / `build.cmd` (`dotnet lambda package` to the zip `serverless.yml` names).
2. `serverless deploy` (README). The README's Silicon sequence uses `--aws-profile pmf`. `serverless.yml` sets `profile: pmf-prod`.
3. `aws-lambda-tools.default.json` sets profile `pmf`, region `eu-west-1`, framework `netcoreapp3.1`.

`setup.sh` installs `Amazon.Lambda.TestTool-3.1` version `0.9.5`. The README example expects `0.10.0`.

## Known dependencies

- AWS: Lambda, API Gateway, Cognito (existing pool), Secrets Manager, SES SMTP, S3, EventBridge schedule, a VPC attachment.
- MySQL via `MySql.Data` 8.0.23. Database name comes from secret field `dbInstanceIdentifier`.
- Upstream HTTP API on a private address, paths under `IForce/` and `infringement/`.
- Google reCAPTCHA siteverify.
- iVeri Lite (return body is parsed as form fields; the hosted page itself is not in this repo).
- A PDF microservice that accepts `{ "html": ... }` and returns a filename/path. The PDF is then read from S3.
- NuGet packages in `AwsDotnetCsharp.csproj`: API Gateway events, Lambda core and JSON serializer, AspNetCoreServer, S3, Secrets Manager, Cognito Identity Provider, Cognito authentication extensions, Newtonsoft.Json 12.0.3. `Select.HtmlToPdf.NetCore` is commented out.
- Node is required for the Serverless Framework CLI. `yarn.lock` is a v1 header with no packages. `renovate.json` extends `config:base`.

## Current project status

**Implemented and present on `master`.** The Lambda surface in `serverless.yml` is the product this repository ships. HEAD is four years older than today.

**Not in this revision.** No test project, no pipeline file, no schema migrations, no API authorizer.

**Remote branches that are not `master`.** These names exist on `origin` and were not checked out: `Dev_Development`, `Github_Dev_Ready`, `conventions`, `cronjob`, `demo_fix`, `fixes`, `fixes_02`, `graphsettings`, `iveriTransactionEdit`, `ls_GetSettings`, `ls_Graphs`, `ls_fixUserId`, `ls_fixes01`, `ls_iveri`, `ls_minorFixes`, `ls_pwdissue`, `ls_receipts`, `ls_todos`, `ls_updateRegistration`, `pmf-backend-dev`, `receipts`. Their contents are **Unknown**.

## Related knowledge

Silicon Ark already has separate directories. Nothing in this repository names them. Route overlap with `paymyfines-backend-new` is corroboration, not a link committed here.

- `paymyfines-backend-new` — later tree (`main` at `c01bc83`, 2025-02-27), service names `tmt-pmf` / `tmt-pmf-prod`, handlers split under `Functions/`.
- `paymyfines-frontend`, `paymyfines-frontend-new`
- `paymyfines-pdf-application`
- `paymyfines-worker`
- `Paymyfines-CronJob`
- `pmf-backend-ah`, `pmf-frontend-ah`

Whether `paymyfines-backend-new` replaced this API is **Unknown / Requires Confirmation**.

## Human confirmation

- Is `master` at `5b98fa7` still what production runs, or has `paymyfines-backend-new` replaced it?
- Is the EventBridge rate of 2 hours the intended schedule? The YAML comment says 8 hours, and the rule name is `RunCronJobDev` on a production service.
- What do `strIdType` values mean? RSA format validation runs for `"2"` on register, update, and transactions, and for `"2"` or `"02"` on `getallfines`. Other numeric types are stored without that check.
- `POST /register` assigns a 400 response when a type `"2"` ID fails validation and then continues. If the email is valid it can still insert the user. Confirm whether that is accepted.
- HTTP routes have no authorizer. Confirm that callers are trusted some other way.
- The reCAPTCHA secret, upstream base URLs, PDF URL, admin mailbox, and secret ARNs are hardcoded. Confirm whether those values are still current. The secret value itself is not copied into this document.
- iVeri success is inferred when `LITE_RESULT_DESCRIPTION` is empty. The code comment says to check that with iVeri.
- The failure mail always addresses `lloyd@overdrive.co.za`, then CCs users whose `strRole` equals the literal `["ADMIN"]`.
