BigBrainParking/parksmarter-client/src/types.ts
Hank 4cb5478737 v0.1.8: tap a session for full detail + receipt
- Sessions list items are now tappable -> SessionDetailScreen.
- Past sessions fetch the full receipt (GET /api/ParkingReceipt): vehicle,
  start/end, time purchased, parking/fee/total, payment (MASTERCARD ••1234),
  auth code, meter, city, transaction id. Adds an "Email me this receipt" action.
- Active sessions show zone/space/start/ends/time-remaining.
- Confirm PastSession + ParkingReceipt types from a live DL-zone session (were
  UNCONFIRMED); fix past-session card to show Zone/Description, not blank.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:38:39 -07:00

682 lines
20 KiB
TypeScript

/**
* ParkSmarter API data models.
*
* Two casing worlds exist:
* - This client's *inputs* use friendly camelCase (see the `*Params` types).
* - The server speaks PascalCase ("PSJSON"). Response models below therefore use
* the exact server field spellings recovered from the app. Response interfaces
* carry an index signature because not every field is guaranteed on every call;
* the well-known fields are typed explicitly. Run a live capture to lock down
* the optional tail for a given endpoint if you need exhaustiveness.
*/
/**
* The common status/token-refresh envelope embedded in most authenticated
* responses as `Response`. `Auth_Token` here is usually null but, when present,
* is a rolling refresh of the user token (the client stores it automatically).
* CONFIRMED via live capture.
*/
export interface ResponseEnvelope {
Auth_Token: string | null;
Message: string | null;
Status: string | null;
}
/**
* Casing note (CONFIRMED via live capture): the meter/zone *response* uses
* `ZoneId` / `SpaceId` / `CustomerId` (lowercase "d"), while estimate & session
* *requests* expect `ZoneID` / `SpaceID` / `CustomerID` (capital "ID"). When you
* feed a captured Zone into an estimate/session, read `zone.CustomerId` /
* `space.SpaceId` and pass them as the client's camelCase params — the client
* emits the capital-ID request spelling for you.
*/
/* ------------------------------------------------------------------ */
/* Auth & account */
/* ------------------------------------------------------------------ */
export interface LoginWithPhoneParams {
/** Phone number, digits only (no country code prefix). */
phoneNumber: string;
password: string;
}
export interface LoginWithAppleParams {
emailAddress: string;
appleId: string;
providerAuth: string;
providerIdentity: string;
}
/**
* POST /api/Auth response (login). CONFIRMED: `{ Status, Message, Auth_Token }`.
* Auth_Token is persisted automatically; SessionId is delivered separately (by
* bootstrap / headers) and also persisted.
*/
export interface AuthResponse {
Status?: string;
Message?: string;
Auth_Token?: string;
[key: string]: unknown;
}
export interface SignUpParams {
emailAddress: string;
/** Phone number, digits only. */
mobilePhone: string;
password: string;
}
export interface RequestResetPasswordParams {
/** Server `ResetPasswordType`; the app passes a numeric/string reason code. */
reqType: string | number;
/** Phone number, digits only. */
phoneNumber: string;
}
export interface UpdatePasswordParams {
oldPassword: string;
newPassword: string;
}
export interface UpdateProfileParams {
emailAddress: string;
/** Phone number, digits only. */
phoneNumber: string;
}
export interface RequestVerifyUserParams {
/** Phone number, digits only. The client prefixes country code "1". */
phoneNumber: string;
}
export interface VerifyUserParams {
/** Phone number, digits only. The client prefixes country code "1". */
phoneNumber: string;
/** SMS confirmation code. */
code: string;
}
export interface UpdateDeviceTokenParams {
/** Push token (server field `DeviceID`). */
pushNotificationsToken: string;
imeiNumber?: string;
/** Defaults to '1' (Android) as the app hardcodes. */
deviceType?: string;
/** BCP-47 language, defaults to the client locale. */
language?: string;
}
export interface UpdateDeviceTokenResponse {
Status?: string;
Message?: string;
[key: string]: unknown;
}
/** GET /api/User response. CONFIRMED via live capture. */
export interface UserDetail {
PersonalEmailAddress?: string;
/** Digits-only phone as a string. */
PersonalPhone?: string;
VehicleDetails?: VehicleDetail[];
CreditCardDetails?: CreditCardDetail[];
/** Numeric flag (0/1), not a boolean. */
OffersOptIn?: number;
[key: string]: unknown;
}
/* ------------------------------------------------------------------ */
/* Vehicles */
/* ------------------------------------------------------------------ */
/** CONFIRMED via live capture. Note `IsDefault` is a string ("true"/"false"), not a boolean. */
export interface VehicleDetail {
VehicleID?: number;
VehiclePlate?: string;
VehicleState?: string;
VehicleAlias?: string;
/** Server returns this as a string, e.g. "true" / "false". */
IsDefault?: string;
/** Per-item status fields, usually null on reads. */
Status?: string | null;
Message?: string | null;
[key: string]: unknown;
}
export interface AddVehicleParams {
plate: string;
state: string;
vehicleAlias?: string;
isDefaultVehicle?: boolean;
}
export interface UpdateVehicleParams extends AddVehicleParams {
id: number | string;
}
export interface VehicleMutationResponse {
Status?: string;
[key: string]: unknown;
}
/* ------------------------------------------------------------------ */
/* Credit cards */
/* ------------------------------------------------------------------ */
/**
* CONFIRMED via live capture. The PAN is never returned — only `CCFirstSix` +
* `CCLastFour`. `CCDefault` comes back as a string ("true"/"false"). Note the
* response uses `ParentPBPCardId`, whereas the add/update *request* uses `ParentCCID`.
*/
export interface CreditCardDetail {
CCID?: number;
CCAlias?: string;
/** First 6 digits (BIN), numeric string. */
CCFirstSix?: string;
/** Last 4 digits, numeric string. */
CCLastFour?: string;
CCExpDate?: string;
CCZip?: string;
/** String "true"/"false", not a boolean. */
CCDefault?: string;
ParentPBPCardId?: number | null;
/** Per-item status fields, usually null on reads. */
Status?: string | null;
Message?: string | null;
[key: string]: unknown;
}
export interface AddCardParams {
cardNumber: string;
alias?: string;
/** Expiry, format as the app sends it (e.g. "MM/YY"). */
expDate: string;
zipCode: string;
isDefaultCard?: boolean;
/** Present when editing an existing card (server `ParentCCID`). */
id?: number | string;
}
export interface SetDefaultCardParams {
id: number | string;
isDefaultCard: boolean;
}
/* ------------------------------------------------------------------ */
/* Meters / zones */
/* ------------------------------------------------------------------ */
export interface LatLng {
latitude: number;
longitude: number;
}
/** A rate/time policy slot within a space. CONFIRMED via live capture. */
export interface SpacePolicy {
CurrentSlot?: boolean;
DayNumber?: number;
DisplayString?: string;
StartDateTime?: string;
EndDateTime?: string;
StartTimeDisplay?: string;
EndTimeDisplay?: string;
EventRateFlag?: boolean;
MaxTime?: number;
MessageHeader?: string;
MessageText?: string;
Rate?: number;
RateType?: string;
[key: string]: unknown;
}
/**
* A single space within a zone. CONFIRMED via live capture.
* Note the id is `SpaceId` (lowercase "d") here; estimate/session requests want
* the capital-ID `SpaceID` (the client maps camelCase params to that spelling).
*/
export interface Space {
SpaceId?: number;
SpaceName?: string;
SSPMSpaceId?: number;
MeterPurchaseType?: number;
OccupancyStatus?: number;
Policies?: SpacePolicy[];
[key: string]: unknown;
}
/**
* A parking zone/meter as returned inside `Zones`. CONFIRMED via live capture.
* `ScannerCode` is the value encoded in kiosk QR codes; `TerminalSerNo` is the
* printed serial. Both drive the scan / lookup features.
*/
export interface Zone {
ZoneId?: number;
ZoneGuid?: string;
ZoneName?: string;
ZoneLocation?: string;
ZonePlates?: unknown;
/** Printed serial number (numeric string). */
TerminalSerNo?: string;
/** QR-encoded scanner code. */
ScannerCode?: string;
Lat?: number;
Long?: number;
CustomerId?: number;
CustomerName?: string;
/** Minutes offset / IANA-less numeric zone id as returned. */
TimeZone?: number;
DSTAdjust?: number;
CityLogo?: string;
DepartmentLogo?: string;
BackgroundColor?: string;
/** Returned as a numeric string. */
ForegroundColor?: string;
MinimumAmount?: number;
MaxTime?: number;
MeterType?: string;
MeterTypeId?: number;
/** Base rate, returned as a string. */
Rate?: string;
ProgressiveRate?: number;
PercentageFull?: number;
IsAllowOverAir?: boolean;
IsAllowBLE?: boolean;
IsExtension?: boolean;
IsPaid?: boolean;
Spaces?: Space[];
[key: string]: unknown;
}
/** GET /api/Meter and /api/MeterList. CONFIRMED via live capture. */
export interface MetersResponse {
Zones?: Zone[] | null;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
/* ------------------------------------------------------------------ */
/* Parking lots (ParkingLogix) */
/* ------------------------------------------------------------------ */
/** One level/section within a ParkingLogix lot. CONFIRMED via live capture. */
export interface ParkingLotDetail {
ParkingLogixLotDetailId?: number;
ParkingLogixLotId?: number;
Level?: number;
TotalSpaces?: number;
FreeSpaces?: number;
[key: string]: unknown;
}
/** CONFIRMED via live capture. Per-level occupancy lives in ParkingLogixLotDetails. */
export interface ParkingLot {
ParkingLogixLotId?: number;
LotName?: string;
LocationAddress?: string;
Lat?: number;
Long?: number;
TotalSpaces?: number;
DefaultFree?: boolean;
ParkingLogixLotDetails?: ParkingLotDetail[];
[key: string]: unknown;
}
export interface ParkingLotsResponse {
ParkingLogixLots?: ParkingLot[] | null;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
/* ------------------------------------------------------------------ */
/* Estimates */
/* ------------------------------------------------------------------ */
export interface MultiEstimateParams {
zoneId: number | string;
spaceId: number | string;
customerId: number | string;
/** Zone minimum credit (server `MinCreditAmount`). */
minCreditAmount?: number | string;
vehicleId: number | string;
/** Optional BLE encrypted bytes string. */
bleEncBytes?: string;
}
export interface SingleEstimateParams {
zoneId: number | string;
spaceId: number | string;
customerId: number | string;
/** Server `ParkingDuration`, in minutes. */
durationInMinutes: number;
vehicleId: number | string;
/** Server `CCID` — credit card id. */
creditCardId: number | string;
}
export interface ItemsEstimateParams {
zoneId: number | string;
spaceId: number | string;
customerId: number | string;
vehicleId: number | string;
}
/**
* One priced duration option. CONFIRMED via live capture.
* Money/id fields come back as numeric *strings* here.
*/
export interface ParkingDetail {
Minutes?: number;
StartTime?: string;
EndTime?: string;
ParkingCost?: string;
TransactionFee?: string;
CurrentAmountPaid?: number;
CustomerID?: string;
CustomerName?: string;
Zone?: string;
ZoneID?: string;
Space?: string;
SpaceID?: string;
VehicleID?: string;
VehicleNumber?: string | null;
CCNumber?: string | null;
PricingToken?: string | null;
TimeZone?: number;
DSTAdjust?: number;
[key: string]: unknown;
}
/** GET /api/ParkingEstimateMulti. CONFIRMED via live capture (a full price ladder). */
export interface MultiEstimateResponse {
ParkingDetail?: ParkingDetail[];
MinTime?: number;
MaxTime?: number;
TimeRemaining?: number;
JumpRateEnabled?: boolean;
JumpRateThreshold?: number;
JumpRateSetValue?: number;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
/** GET /api/ParkingEstimate. CONFIRMED via live capture (a single priced option). */
export interface SingleEstimateResponse {
ParkingDetail?: ParkingDetail;
MinTime?: number;
MaxTime?: number;
CurrentCustomerDateTime?: string;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
/** GET /api/ParkingEstimateItems. CONFIRMED via live capture. */
export interface ItemsEstimateResponse {
ZoneID?: number;
ZoneName?: string;
SpaceID?: number;
SpaceName?: string;
CustomerID?: number;
CustomerName?: string;
PurchaseMode?: string;
Items?: unknown[] | null;
TimeStampUTC?: number;
TimeZone?: number;
DSTAdjust?: number;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
/* ------------------------------------------------------------------ */
/* Parking sessions */
/* ------------------------------------------------------------------ */
export interface StartParkingSessionParams {
creditCardId: number | string;
spaceId: number | string;
customerId: number | string;
zoneId: number | string;
vehicleId: number | string;
/** ISO/formatted start time as the server expects. */
startTime: string;
endTime: string;
/** Minutes purchased (server `TimePurchased`). */
minutesToPurchase: number;
/** Base cost, dollars (server `ParkingCost`). */
parkingCost: number;
/** Fee, dollars (server `TransactionFee`). Defaults to 0. */
transactionFee?: number;
/** Zone minimum credit (server `MinCreditAmount`). */
minCreditAmount?: number;
meterTypeId: number | string;
/** Optional BLE encrypted bytes string. */
bleEncBytes?: string;
}
/**
* POST /api/Session. CONFIRMED via live capture. Returns HTTP 200 even on a
* declined charge — check `Response.Status === 'Error'` (the client does this and
* throws). `OriginalErrorMessage` carries the gateway reason (e.g. "DECLINED").
*/
export interface StartParkingSessionResponse {
Response?: ResponseEnvelope;
BleEncBytes?: string | null;
PBPParkingSessionId?: number;
OriginalErrorMessage?: string;
StartTime?: string | null;
EndTime?: string | null;
TimePurchased?: string | null;
ZoneID?: number | string | null;
Zone?: string | null;
SpaceID?: number | string | null;
Space?: string | null;
VehicleNumber?: string | null;
Lat?: number | null;
Long?: number | null;
TransactionFee?: string | number | null;
Amount?: string | number | null;
AmountCharged?: string | number | null;
[key: string]: unknown;
}
/** UNCONFIRMED element shape (no active sessions on the test account). */
export interface ActiveSession {
TransactionID?: number | string;
ZoneName?: string;
SpaceName?: string;
Space?: string;
StartTime?: string;
EndTime?: string;
StartTimeDisplay?: string;
EndTimeDisplay?: string;
TimeRemaining?: number | string;
VehiclePlate?: string;
VehicleNumber?: string;
Amount?: number | string;
IsExtension?: boolean;
[key: string]: unknown;
}
/** GET /api/ParkingSession. Envelope CONFIRMED; element shape UNCONFIRMED (no active sessions in test account). */
export interface ActiveSessionsResponse {
ParkingSession?: ActiveSession[] | null;
CurrentCustomerTime?: string | null;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
/** CONFIRMED via a live DL-zone session (fields observed on GET /api/Session). */
export interface PastSession {
TransactionID?: number | string;
Zone?: string;
ZoneID?: number | string;
Space?: string;
SpaceID?: number | string;
StartTime?: string;
EndTime?: string;
TimePurchased?: number | string;
VehicleNumber?: string;
Amount?: number | string;
TransactionFee?: number | string;
CardFirstSix?: string;
CardLastFour?: string;
PaymentDisplay?: string;
CustomerID?: number | string;
CustomerName?: string;
City?: string;
Description?: string;
MeterTypeId?: number;
Lat?: number | string;
Long?: number | string;
IsFavorite?: boolean;
FavoriteID?: number | string;
Logo?: string;
/** Legacy/alt field some views used; prefer `Zone`. */
ZoneName?: string;
[key: string]: unknown;
}
/** GET /api/Session. Envelope CONFIRMED; element shape UNCONFIRMED (no past sessions in test account). */
export interface PastSessionsResponse {
Session?: PastSession[] | null;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
export interface PastSessionsParams {
currentPage?: number | string;
pageSize?: number | string;
}
/* ------------------------------------------------------------------ */
/* Receipts */
/* ------------------------------------------------------------------ */
/** CONFIRMED via a live DL-zone receipt (GET /api/ParkingReceipt). */
export interface ParkingReceipt {
TransactionID?: number | string;
CustomerID?: number | string;
CustomerName?: string;
Space?: string;
SpaceID?: number | string;
MeterNumber?: string;
MeterTypeId?: number;
StartTime?: string;
EndTime?: string;
PaymentType?: string;
PaymentDisplay?: string;
CardType?: string | null;
/** Masked card, e.g. "****1234". */
CC?: string;
AuthCode?: string;
VehicleID?: number | string;
Vehicle?: string;
/** Display strings, e.g. "$0.10". */
Amount?: string;
AmountCharged?: string;
TransactionFee?: string;
Total?: string;
/** Numeric equivalents. */
AmountValue?: number;
TransactionFeeValue?: number;
TotalValue?: number;
Logo?: string;
BackgroundColor?: string;
ForegroundColor?: string;
ZoneName?: string;
[key: string]: unknown;
}
export interface ReceiptResponse {
ParkingReceipt?: ParkingReceipt;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
/* ------------------------------------------------------------------ */
/* Notification settings */
/* ------------------------------------------------------------------ */
/** One toggle group within notification settings. */
export interface NotificationSetting {
messagesEnabled?: boolean;
emailsEnabled?: boolean;
messages?: boolean;
emails?: boolean;
}
/** Client-side view of the settings (server sends a numeric bitmask in `Settings`). */
export interface NotificationSettings {
parkingReceipt?: NotificationSetting;
overnight?: NotificationSetting;
tenMin?: NotificationSetting;
fifteenMin?: NotificationSetting;
sessionEnded?: NotificationSetting;
sessionStart?: NotificationSetting;
}
/** GET /api/Setting. CONFIRMED: `{ Response, Settings }`. */
export interface RawSettingsResponse {
/** Server bitmask as a numeric string. Decode per the app's expandSettings logic. */
Settings?: string;
Response?: ResponseEnvelope;
[key: string]: unknown;
}
/** GET /api/State?ID=…. CONFIRMED wrapper `{ StatesResult }` (requires an ID; 404s without). */
export interface StatesResponse {
StatesResult?: StateItem[];
[key: string]: unknown;
}
/* ------------------------------------------------------------------ */
/* Content / misc */
/* ------------------------------------------------------------------ */
/** Terms/Privacy/About all return `{ Value: string }`. */
export interface ValueResponse {
Value?: string;
[key: string]: unknown;
}
export interface FAQItem {
Question?: string;
Answer?: string;
[key: string]: unknown;
}
export interface FAQResponse {
FAQs?: FAQItem[];
[key: string]: unknown;
}
/** GET /api/ApplicationValidity — feature flags + maintenance + session bootstrap. */
export interface ApplicationValidityResponse {
SessionId?: string;
RecommendUpgrade?: boolean;
ForceUpgrade?: boolean;
IsParkingEstimateMultiEnabled?: boolean;
Config?: {
IsInMaintenanceMode?: boolean;
ExtendedVariablePBPZoneIds?: unknown;
ExtendedVariableRateMaxDayMinutes?: number;
ExtendedVariableRateMaxDay?: number;
ExtendedVariableMultiDayValueThreshold?: number;
ExtendedVariableRateSingleHourRate?: number;
ExtendedVariableRateMaxDayJump?: number;
ExtendedVariableRateMaxDayValueDollar?: number;
ExtendedVariableRateMaxMode?: unknown;
UseLimitedMetersFetch?: boolean;
UseLoadMetersButton?: boolean;
UseReloadMetersOnResume?: boolean;
UseApplePay?: boolean;
UseGooglePay?: boolean;
ApplePayHostAddress?: string;
UseSearchBySpace?: boolean;
[key: string]: unknown;
};
[key: string]: unknown;
}
export interface StateItem {
[key: string]: unknown;
}