Files
card-to-iban/README.md

5.4 KiB

Card to IBAN

A small full-stack application that converts an Iranian bank card number to its IBAN using the ZarinHub API. The backend is written in Go with the standard HTTP library, while the frontend uses Nuxt, Vue, and Tailwind CSS.

The request flow is intentionally simple:

Nuxt UI -> HTTP handler -> Card service -> ZarinHub client
                    |
                    -> Audit repository -> SQLite

The HTTP handler coordinates the public API contract and audit persistence. The service validates card numbers, the ZarinHub client handles external requests, and the repository contains the SQLite-specific implementation.

Prerequisites

  • Go 1.27 or later
  • Node.js 22 or later
  • A valid ZarinHub access token

Configuration

Copy the example environment file from the project root:

Copy-Item .env.sample .env

Set ZARINHUB_ACCESS_TOKEN in the new .env file. This file is ignored by Git.

Variable Default Purpose
ZARINHUB_ACCESS_TOKEN None; required ZarinHub access token
ZARINHUB_ENDPOINT Official CardToIban endpoint External service endpoint
ZARINHUB_TIMEOUT 5s External request timeout
DATABASE_PATH ./card-to-iban.db SQLite database file path
SERVER_ADDR 127.0.0.1:8080 Backend HTTP address
FRONTEND_ORIGIN http://127.0.0.1:3000 Allowed frontend origin

The frontend reads the following value from frontend/.env and uses the shown address by default:

NUXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080

Running the Project

Start the backend from the Go module directory. The root .env file is loaded automatically, and the database migration runs during startup:

cd backend
go run ./cmd/api

In another terminal, install the frontend dependencies and start the development server:

cd frontend
npm install
npm run dev

The frontend is available at http://127.0.0.1:3000. The backend health endpoint is available at http://127.0.0.1:8080/health.

API Contract

Convert a card number to an IBAN

POST /api/v1/card-to-iban
Content-Type: application/json

{"card":"6037997512345678"}

Successful response:

{
  "data": {
    "iban": "IR820540102680020817909002"
  },
  "requestId": "SERVER_GENERATED_REQUEST_ID"
}

All errors use the same response structure. Error messages are returned in Persian to match the user interface:

{
  "error": {
    "code": "invalid_card",
    "message": "شماره کارت باید شامل ۱۶ رقم باشد."
  },
  "requestId": "SERVER_GENERATED_REQUEST_ID"
}

The same request ID is also returned in the X-Request-ID response header.

HTTP status Stable error code Meaning
400 invalid_card The JSON body or card number format is invalid
405 method_not_allowed The request method is not POST
422 conversion_rejected ZarinHub rejected the conversion
502 upstream_failure ZarinHub authentication failed or its response was invalid
503 upstream_unavailable The upstream service is temporarily unavailable
504 upstream_timeout The upstream request timed out
500 internal_error Audit persistence or another internal operation failed

Auditing and Data Security

  • Every conversion attempt creates one audit record, including invalid requests and upstream failures.
  • A response is returned only after its audit record is saved successfully. A database failure produces a generic 500 response.
  • Full card numbers are never written to the database or logs. Stored values are masked, for example 603799******5678.
  • ZarinHub JSON responses are sanitized recursively before storage, and every field named card is masked.
  • Non-JSON upstream responses are not stored because their contents cannot be safely verified.
  • Client IP addresses are intentionally not collected, so the related audit column remains empty.
  • Structured logs contain only the request ID, route, HTTP status, audit outcome, and duration.
  • Request bodies are limited to 1 KiB, and ZarinHub response bodies are limited to 1 MiB.

Testing

Backend tests do not use real credentials. They use fakes and httptest for ZarinHub requests:

cd backend
go test ./...
go vet ./...

Run the frontend tests and production build with:

cd frontend
npm test
npm run build

The test suite covers card validation and masking, the conversion service, the ZarinHub client, response-size limits, the SQLite repository, JSON sanitization, HTTP outcomes, audit failures, Persian and Arabic digit normalization, and frontend success and error states.

Assumptions and Limitations

  • Backend validation intentionally accepts only exactly 16 ASCII digits. Luhn validation is outside the assessment requirements.
  • The frontend normalizes Persian and Arabic digits and removes spaces or hyphens before submission.
  • SQLite is sufficient for this assessment and uses a single writer connection for predictable behavior.
  • Only the IBAN is exposed to the frontend. Account details, owner information, and raw ZarinHub responses remain internal.
  • Retries, Docker, OpenAPI, CI, dark mode, and production infrastructure are intentionally outside the project scope.
  • The interface uses the existing ZarinPal design system's font and logo assets. The font license is stored in frontend/public/fonts.