Implementing P2P Trading in a Mobile Exchange App
P2P trading is direct crypto-to-fiat exchange between users with escrow protection from the exchange. Buyer transfers fiat to seller (via bank, cash, payment system), seller confirms — exchange releases crypto from escrow to buyer. Architecturally, it's a messenger plus trading platform plus arbitration system in one app.
Listing Ads: Filters and Sorting
The ads page is the key P2P screen. Users see a list of offers with price, limits, payment methods, and seller rating.
Filters: amount (fiat), payment method (Tinkoff, Sberbank, QIWI, cash, etc.), fiat currency. Sort by price. On load, use pagination with cursor (not offset) since the list changes in real-time.
Binance P2P API (unofficial): POST https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search. Official exchanges with P2P provide their own APIs — structure is similar.
// Android — fetching P2P ads listing
data class P2PSearchRequest(
val asset: String, // BTC, USDT, ETH
val fiat: String, // RUB, USD, EUR
val tradeType: String, // BUY or SELL
val payTypes: List<String>, // ["Tinkoff", "RaiffeisenBank"]
val page: Int = 1,
val rows: Int = 20,
val transAmount: String = "" // transaction sum to filter by limits
)
Trade Flow: 7 States
P2P trades are a finite state machine:
| State | Action | Who |
|---|---|---|
CREATED |
Order created, crypto locked in escrow | System |
WAITING_PAYMENT |
Awaiting fiat payment | Buyer |
PAID |
Buyer clicked "Paid" | Buyer |
RELEASING |
Seller verifying receipt | Seller |
COMPLETED |
Seller confirmed, crypto sent | System |
APPEALING |
Dispute opened (appeal) | Either |
CANCELLED |
Cancelled | Either |
In mobile UI — trade screen with timer, instructions for current stage, and action buttons. Payment timer (usually 15–30 min) is critical: if it expires, the order auto-cancels.
Embedded Chat
Chat within a trade is mandatory. Seller and buyer must agree on details; buyer can send payment proof screenshots.
Minimum chat: WebSocket for real-time messages, image support (receipt screenshots), system messages about trade status changes.
// iOS — P2P chat message model
struct P2PChatMessage: Identifiable, Codable {
let id: String
let orderId: String
let senderId: String
let messageType: MessageType // text / image / system
let content: String
let imageUrl: String?
let timestamp: Date
var isSystemMessage: Bool { messageType == .system }
}
Images are critical: users send bank transfer screenshots as payment proof. Need photo upload with full-screen preview. Store screenshots for arbitration.
Push Notifications and Timers
P2P without notifications is dead functionality. Mandatory events:
- New chat message — immediately
- Buyer clicked "Paid" — immediately to seller
- Timer expires in 5 min — to both
- Appeal opened — to both
Deep link from notification opens the specific trade screen by orderId.
Appeal (Dispute) and Arbitration
On conflict, either party can open a dispute. UI shows button "Open Appeal" with mandatory reason selection and evidence attachment (screenshots). After opening, chat freezes; exchange arbitrator joins.
Mobile must support uploading multiple images for evidence. On iOS — UIImagePickerController or PHPickerViewController (iOS 14+). On Android — ActivityResultContracts.GetMultipleContents.
Seller Rating and Verification
Seller card: trade completion %, trades in 30 days, first response time, verification badges (KYC, Email, SMS). Directly impacts trust — show full stats, don't hide.
Timeline: MVP P2P module — 6–10 weeks. Includes ad listing with filters, trade flow with timer, embedded chat with images, push notifications. Without arbitration system and seller verification — 4–5 weeks.







