Card Verification Flow - Complete Implementation
Overview
This document describes the complete card verification flow that automatically verifies or rejects cards based on IBAN ownership.
Flow Summary
User (TMA) → Gateway → Payment Service → PayStar APIs → Auto-Approve/Reject
Detailed Flow
1. User Submits Card (TMA)
Endpoint: POST /api/v1/card/verify
Request:
{
"card_number": "6104337952695920"
}
Headers:
- Authorization: Bearer <JWT_TOKEN>
- X-Telegram-ID: 39459186
2. Gateway Handler Processing
File: services/gateway/internal/handlers/card.go
Steps:
1. Validate card number (16 digits)
2. Get user info from database (first_name, last_name from Level 1 verification)
3. Call Payment Service /api/v1/payment/card-to-iban
4. Receive IBAN + owners list
5. Auto-verify logic:
- Compare user’s first_name + last_name with all IBAN owners
- If match found → status = "verified", set verified_at
- If no match → status = "rejected", set rejection_reason
- If no owners returned → status = "pending_verification" (manual review)
6. Insert into card_verifications table
7. Return result to user
3. Payment Service Processing
File: services/payment/internal/payment/service/payment_service.go
Method: GetIBANFromCard(ctx, cardNumber) -> CardVerificationResult
Steps:
1. Get PayStar provider
2. Step 1: Call CardToIban API
- Endpoint: https://core.paystar.ir/api/open-banking/service/bank-inquiry/card-to-iban
- Get IBAN from card number
3. Step 2: Call IbanInquiry API
- Endpoint: https://core.paystar.ir/api/open-banking/service/bank-inquiry/iban-inquiry
- Get owners list and bank name
4. Return combined result
4. PayStar Provider Implementation
File: services/payment/internal/providers/paystar/statement_client.go
Card-to-IBAN API
func (p *PaystarProvider) CardToIban(ctx context.Context, cardNumber string) (string, error)
Request:
{
"application_id": "pq5np",
"access_password": "WbBeLbadDbEOakhpigaGlgFVUrwtB3iCOABRQPhZq6o02yODST",
"card": "6104337952695920"
}
Response:
{
"status": 1,
"data": {
"iban": "IR340190000000120287328006",
"response_variant": 0
},
"message": "درخواست با موفقیت انجام شد"
}
IBAN Inquiry API
func (p *PaystarProvider) IbanInquiry(ctx context.Context, iban string) (*providers.IBANInquiryResult, error)
Request:
{
"application_id": "pq5np",
"access_password": "WbBeLbadDbEOakhpigaGlgFVUrwtB3iCOABRQPhZq6o02yODST",
"iban": "IR340190000000120287328006"
}
Response:
{
"status": 1,
"data": {
"iban": "IR340190000000120287328006",
"owners_info": [
{
"firstName": "علی",
"lastName": "احمدی"
}
],
"bank_title": "بانک ملی"
},
"message": "درخواست با موفقیت انجام شد"
}
5. Name Matching Logic
Function: namesMatch(firstName1, lastName1, firstName2, lastName2) -> bool
Matching Rules: 1. Exact match (case-insensitive) 2. Reversed order (family name first vs last) 3. Full name concatenated match 4. Normalized (trimmed, lowercased)
Example Matches: - “Ali” + “Ahmadi” = “ali” + “ahmadi” ✓ - “Ali” + “Ahmadi” = “Ahmadi” + “Ali” ✓ (reversed) - “علی” + “احمدی” = “علی” + “احمدی” ✓
6. Database Schema
Table: card_verifications
CREATE TABLE card_verifications (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
card_number TEXT NOT NULL,
card_number_hash TEXT NOT NULL,
cardholder_name TEXT,
iban TEXT,
bank_name TEXT,
status TEXT NOT NULL, -- 'pending_verification', 'verified', 'rejected'
verified_at TIMESTAMP,
rejection_reason TEXT,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
expires_at TIMESTAMP
);
7. Response to User
Success (Auto-Verified):
{
"id": "uuid",
"status": "verified",
"card_number": "6104337952695920",
"cardholder_name": "علی احمدی",
"iban": "IR340190000000120287328006",
"bank_name": "بانک ملی",
"verified_at": "2025-11-20T19:00:00Z",
"created_at": "2025-11-20T19:00:00Z",
"message": "Card verified successfully"
}
Rejected (Name Mismatch):
{
"id": "uuid",
"status": "rejected",
"card_number": "6104337952695920",
"cardholder_name": "علی احمدی",
"iban": "IR340190000000120287328006",
"bank_name": "بانک ملی",
"verified_at": null,
"created_at": "2025-11-20T19:00:00Z",
"message": "Card verification rejected: cardholder name does not match IBAN owner"
}
Configuration
Environment Variables
# PayStar Configuration
PAYSTAR_APPLICATION_ID=pq5np
PAYSTAR_ACCESS_PASSWORD=WbBeLbadDbEOakhpigaGlgFVUrwtB3iCOABRQPhZq6o02yODST
PAYSTAR_REFRESH_TOKEN=oPcEe6kFVGNyjlwfegnJu1WwLhN8aNuAE1lVtG0uiV5C1KrsbEUowg4rxhZu2zwt6rE0xG2pbCQjBZ15nnpKXAC0A2xmHCjsjaNx
PAYSTAR_BASE_URL=https://core.paystar.ir
PAYSTAR_ENABLED=true
PAYSTAR_COMPANY_ACCOUNT=0120287328006
PAYSTAR_COMPANY_IBAN=IR340190000000120287328006
Status Values
| Status | Description |
|---|---|
pending_verification |
Awaiting manual admin review (no owners returned) |
verified |
Automatically verified (name matched) |
rejected |
Automatically rejected (name mismatch) |
TMA Integration
Check Card Status
Endpoint: GET /api/v1/card/verify?id=<verification_id>
Response:
{
"id": "uuid",
"card_number": "6104337952695920",
"iban": "IR340190000000120287328006",
"bank_name": "بانک ملی",
"status": "verified",
"verified_at": "2025-11-20T19:00:00Z",
"created_at": "2025-11-20T19:00:00Z"
}
List User Cards
Endpoint: GET /api/v1/card/list
Response:
{
"cards": [
{
"id": "uuid",
"card_number": "6104337952695920",
"iban": "IR340190000000120287328006",
"bank_name": "بانک ملی",
"status": "verified",
"verified_at": "2025-11-20T19:00:00Z",
"created_at": "2025-11-20T19:00:00Z"
}
]
}
Testing
Test Card Verification
# 1. Get JWT token from TMA login
TOKEN="your_jwt_token"
TELEGRAM_ID=39459186
# 2. Submit card verification
curl -X POST https://api.nextgiti.cloud/api/v1/card/verify \
-H "Authorization: Bearer $TOKEN" \
-H "X-Telegram-ID: $TELEGRAM_ID" \
-H "Content-Type: application/json" \
-d '{"card_number": "6104337952695920"}'
# 3. Check status
curl -X GET "https://api.nextgiti.cloud/api/v1/card/verify?id=<verification_id>" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Telegram-ID: $TELEGRAM_ID"
Next Steps (Fiat Deposit/Withdrawal)
After card verification is working in production:
- Fiat Deposit: User deposits IRT to verified IBAN, system credits wallet
- Fiat Withdrawal: User withdraws IRT from wallet to verified card/IBAN
- PayStar Bank Transfer: Use verified IBANs for payouts
Files Changed
/services/gateway/internal/handlers/card.go- Card verification handler with auto-approve/reject/services/payment/internal/payment/service/payment_service.go- Card-to-IBAN + IBAN inquiry service/services/payment/internal/payment/transport/http/handler.go- Card-to-IBAN HTTP handler/services/payment/internal/payment/transport/http/routes.go- Added card-to-IBAN route/services/payment/internal/providers/provider.go- Added IBANInquiry interface/services/payment/internal/providers/paystar/statement_client.go- Implemented PayStar APIs
Deployment
# Build and deploy gateway
cd /opt/gitinext/stacks/gitinext-golang
make build-gateway && make docker-build-gateway && make docker-push-gateway
docker service update --force gitinext-golang_gateway
# Build and deploy payment service
cd services/payment
CGO_ENABLED=0 GOOS=linux go build -o bin/payment ./cmd/server/main.go
cd ../..
docker build -t registry.nextgiti.cloud/payment:latest -f services/payment/Dockerfile .
docker push registry.nextgiti.cloud/payment:latest
docker service update --force gitinext-golang_payment