# Awash Bank

> Detail page: public receipt URL, page structure, captured fields, and return shape.

Awash Bank's `awashpay.awashbank.com:8225` is a server-rendered static HTML "VAT Invoice / Customer Receipt" page. We fetch it directly and scrape the structured tables into JSON.

## URL format

```
https://awashpay.awashbank.com:8225/-<TOKEN>
```

| Part | Pattern | Example |
| --------- | --------------------------------------- | ------------------------ |
| host | literal | `awashpay.awashbank.com` |
| port | literal | `:8225` |
| `<TOKEN>` | leading dash + alphanumerics-and-dashes | `-ABCD1234EF-1A2B3C` |

The leading dash on the path **is part of the URL**; every Awash receipt link starts with one. The token interior carries one or more additional dashes.

## What we actually hit

The URL is passed through unchanged: no SPA bypass, no API endpoint substitution. A direct `GET` returns the receipt HTML in ~250 ms.

## Return type

`receipt.source === "awash-html"`. The static company-info block (TIN `0000030100`, VAT Reg `17264`, etc.) is dropped; it's identical on every Awash receipt. We capture two sub-objects: `customer` (the receipt holder) and `transaction` (the payment itself).

```ts
interface AwashReceipt {
	source: 'awash-html';

	customer: {
		customerName?: string; // e.g. "<customer name>"
		accountNo?: string; // masked: "XXXXX******XXXX/BANK"
		city?: string; // e.g. "Addis Ababa"
		vatRegNo?: string; // "-" when the customer isn't VAT-registered
		vatRegDate?: string;
		tinTaxId?: string; // labelled "TIN (TAX ID)" on the receipt
		branch?: string; // e.g. "<branch name>"
	};

	transaction: {
		transactionTime?: string; // "YYYY-MM-DD HH:MM:SS AM|PM"
		transactionType?: string; // see "Supported transaction types" below
		amount?: string; // string with currency: "<amount> ETB"
		vat?: string; // string with currency: "<vat> ETB"
		charge?: string; // service charge; present on wallet/bill receipts
		senderName?: string;
		senderAccount?: string; // masked: "XXXXX*******XXX"
		beneficiaryName?: string;
		beneficiaryAccount?: string;
		beneficiaryBank?: string; // e.g. "Commercial Bank of Ethiopia"
		reason?: string; // e.g. "Transfer"
		transactionId?: string; // 15-digit numeric (placeholder: "000000000000000")

		// Any label found in the transaction table that didn't map to one of the
		// typed fields above, e.g. "Branch", "CBS Trans ID" on WITHDRAW/DEPOSIT;
		// "Student Code", "Student Class", "School" on School Fees. Absent when
		// every label resolved to a typed field (IPS Bank Transfer, etc.).
		extra?: Record<string, string>;
	};
}
```

Not every field is set on every receipt; Awash issues at least nine distinct layouts depending on `transactionType`, each carrying a different subset.

## Supported transaction types

Awash uses synonym labels across receipt types (`Sender Name` / `Customer Name` / `Name`, `Beneficiary Account` / `Phone Number` / `CA Number`, etc.). The parser maps each synonym into the same typed field below. Type-specific labels we don't have first-class names for surface inside `transaction.extra`.

| transactionType | `senderName` source | `beneficiaryName` / `…Account` source | Lands in `extra` |
| ---------------------------- | ------------------- | ------------------------------------- | --------------------------------------------------- |
| `IPS Bank Transfer` | Sender Name | Beneficiary Name + Account + Bank | n/a |
| `Telebirr Transfer` | Customer Name | n/a / Phone Number | n/a |
| `Send Money` | _customer fallback_ | Recipient | n/a |
| `Bill Payment` | Customer Name | n/a / CA Number | Invoice Number, Invoice Date |
| `Ethiotelecom Postpaid Bill` | Customer Name | n/a / Service Number | n/a |
| `Bulk Payment` | Sender | Recipient | n/a |
| `WITHDRAW` | _customer fallback_ | n/a | Branch, CBS Trans ID |
| `DEPOSIT` | _customer fallback_ | n/a | Branch, CBS Trans ID |
| `School Fees` | _customer fallback_ | Student Name | Student Code, Student Class, School, Payment Period |

### `senderName` fallback

For receipt types where Awash doesn't include a separate sender field (`WITHDRAW`, `DEPOSIT`, `Send Money`, `School Fees`), the receipt-holder is implicitly the originating party, so `transaction.senderName` falls back to `customer.customerName`. This guarantees the validation rule below holds across all nine types; you can detect whether the fallback fired by comparing `transaction.senderName === customer.customerName`.

## Samples

### IPS Bank Transfer

```json
{
	"source": "awash-html",
	"customer": {
		"customerName": "<customer name>",
		"accountNo": "XXXXX******XXXX/BANK",
		"city": "Addis Ababa",
		"vatRegNo": "-",
		"vatRegDate": "-",
		"tinTaxId": "-",
		"branch": "<branch>"
	},
	"transaction": {
		"transactionTime": "2026-01-01 00:00:00 AM",
		"transactionType": "IPS Bank Transfer",
		"amount": "100 ETB",
		"vat": "0.09 ETB",
		"senderName": "<sender name>",
		"senderAccount": "XXXXX*******XXX",
		"beneficiaryName": "<beneficiary name>",
		"beneficiaryAccount": "1000000000000",
		"beneficiaryBank": "Commercial Bank of Ethiopia",
		"reason": "Transfer",
		"transactionId": "000000000000000"
	}
}
```

### Telebirr Transfer

```json
{
	"source": "awash-html",
	"customer": {
		"customerName": "<customer name>",
		"accountNo": "XXXXX******XXXX/BANK",
		"city": "Addis Ababa",
		"branch": "<branch>"
	},
	"transaction": {
		"transactionTime": "2026-01-01 00:00:00 AM",
		"transactionType": "Telebirr Transfer",
		"amount": "100 ETB",
		"vat": "0.09 ETB",
		"charge": "1 ETB",
		"senderName": "<customer name>",
		"senderAccount": "XXXXX*******XXX",
		"beneficiaryAccount": "251********",
		"reason": "Transfer",
		"transactionId": "000000000000000"
	}
}
```

### School Fees (with `extra`)

```json
{
	"source": "awash-html",
	"customer": { "customerName": "<customer name>", "branch": "<branch>" },
	"transaction": {
		"transactionTime": "2026-01-01 00:00:00 AM",
		"transactionType": "School Fees",
		"amount": "100 ETB",
		"senderName": "<customer name>",
		"beneficiaryName": "<student name>",
		"transactionId": "000000000000000",
		"extra": {
			"Student Code": "<student code>",
			"Student Class": "<student class>",
			"School": "<school name>",
			"Payment Period": "<payment period>"
		}
	}
}
```

### WITHDRAW (with `extra`)

```json
{
	"source": "awash-html",
	"customer": { "customerName": "<customer name>", "branch": "<branch>" },
	"transaction": {
		"transactionTime": "2026-01-01 00:00:00 AM",
		"transactionType": "WITHDRAW",
		"amount": "100 ETB",
		"senderName": "<customer name>",
		"senderAccount": "XXXXX*******XXX",
		"transactionId": "000000000000000",
		"extra": {
			"Branch": "<branch>",
			"CBS Trans ID": "<trans id>"
		}
	}
}
```

## Quirks

- **The receipt page is a static HTML render**: no SPA hydration needed, `curl` returns the full page. The embedded QR code re-encodes the same URL the page was served from.
- **Amounts are strings with currency suffix**: `"1800 ETB"`, not `1800`. Awash ships them this way; we preserve verbatim so the caller can decide whether to strip ` ETB` or keep it.
- **Duplicate field labels**: "VAT Reg No" and "VAT Reg Date" appear in both the company-info section (dropped) and the customer-info section (captured). The parser slices by the second `<table class="info-table">` to avoid the company's static `17264` shadowing the customer's `-`.
- **Multi-payee beneficiary names**: Awash receipts for batch transfers contain `&amp;`-separated names (e.g. `"<NAME-1> &<NAME-2> &<NAME-3>"`). HTML entities are decoded before return.
- **Two different masks for the same account**: the receipt-holder's account on `customer.accountNo` carries one mask shape (e.g. `XXXXX******XXXX/BANK` with the `/BANK` suffix), while `transaction.senderAccount` carries a slightly different one (e.g. `XXXXX*******XXX`, no suffix). Awash masks them at the source; we don't unmask or re-mask.
- **`transaction.extra` is type-specific**: same parser, different layout per `transactionType`. If you only consume the typed fields, `extra` can be ignored; if you need to render the full receipt faithfully, iterate over `extra` and render its `(key, value)` pairs alongside.
- **Validation rule**: receipt is considered valid only when `transaction.transactionId`, `transaction.senderName`, AND `transaction.amount` are all present. With the customer-fallback for `senderName`, this passes for all nine receipt types whenever the upstream HTML is complete.
