Location Services
Learn how to work with physical locations where Paysera services are available, including cash-in, cash-out, and payment services.
What are Locations?β
Locations represent physical places where users can:
- π° Cash In - Deposit cash into Paysera account
- π΅ Cash Out - Withdraw cash from Paysera account
- π³ Pay - Make payments with Paysera account
- π Identify - Verify user identity
Each location has:
- Address & GPS coordinates
- Working hours
- Available services
- Pricing information
- Category (for payment locations)
Get All Locationsβ
Retrieve locations with optional filtering by coordinates, date, and status.
Request
GET /rest/v1/locations?locale={locale}&lat={lat}&lng={lng}&distance={distance}&updated_after={timestamp}&status={status}&limit={limit}&offset={offset}
View Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
locale | string | β οΈ Recommended | Language for descriptions (e.g., en, lt) |
lat | float | β¬ Optional | Latitude in decimal format |
lng | float | β¬ Optional | Longitude in decimal format |
distance | integer | β¬ Optional | Distance in meters (default: 500, requires lat & lng) |
updated_after | integer | β¬ Optional | Unix timestamp for filtering by last edit date |
status | string | β¬ Optional | Filter by status: active, inactive (default: active) |
limit | integer | β¬ Optional | Max results (default: 20, max: 200) |
offset | integer | β¬ Optional | Skip results (default: 0) |
This endpoint can be called without authentication.
Response
{
"locations": [
{
"id": 48,
"project_id": 123,
"title": "Paysera Office",
"updated_at": 1382969469,
"status": "active",
"address": "MΔnulio g. 7, Vilnius, Lithuania",
"lat": 54.668516,
"lng": 25.235055,
"services": {
"pay": {
"available": true,
"categories": [1]
},
"cash_in": {
"available": true,
"types": ["contact", "document"]
}
}
}
],
"_metadata": {
"total": 1,
"offset": 0,
"limit": 20
}
}
View Location Object Fields
| Field | Type | Description |
|---|---|---|
id | integer | Location ID |
project_id | integer | Associated project ID |
title | string | Location name |
updated_at | integer | Unix timestamp of last update |
status | string | active or inactive |
description | string | Location description (optional) |
address | string | Physical address (optional) |
lat | float | Latitude (optional) |
lng | float | Longitude (optional) |
radius | integer | Location radius in meters (optional) |
prices | array | Service pricing information (optional) |
working_hours | object | Opening hours by weekday (optional) |
services | object | Available services (optional) |
images | object | Map pin images |
remote_orders | object | Spot ID for remote orders (optional) |
Example: Find Nearby Locations
async function findNearbyLocations(lat, lng, distanceMeters = 2000) {
const response = await api.getLocations({
locale: 'en',
lat: lat,
lng: lng,
distance: distanceMeters,
limit: 50
});
console.log(`Found ${response.locations.length} locations within ${distanceMeters}m`);
return response.locations.map(loc => ({
id: loc.id,
name: loc.title,
address: loc.address,
distance: calculateDistance(lat, lng, loc.lat, loc.lng),
services: Object.keys(loc.services || {}).filter(
s => loc.services[s].available
)
}));
}
// Usage - find locations near Vilnius center
const nearby = await findNearbyLocations(54.687157, 25.279652, 2000);
nearby.forEach(loc => {
console.log(`${loc.name} - ${loc.distance}m - ${loc.services.join(', ')}`);
});
Available Servicesβ
Cash In Service
Deposit cash into Paysera account.
Service types:
| Type | Description |
|---|---|
contact | With contact info (email/phone) verification |
bar_code | Using multi-use barcode |
document | With ID document verification |
one_time_bar_code | Using single-use barcode |
Example:
{
"cash_in": {
"available": true,
"types": ["contact", "document", "bar_code"]
}
}
Cash Out Service
Withdraw cash from Paysera account.
Service types:
| Type | Description |
|---|---|
qr_code | Using QR code |
document | With ID document verification |
Example:
{
"cash_out": {
"available": true,
"types": ["qr_code", "document"]
}
}
Pay Service
Make payments at location.
Features:
- Category-based locations
- Special offers
- Loyalty programs
Example:
{
"pay": {
"available": true,
"categories": [1, 2, 5]
}
}
Identification Service
User identity verification.
Example:
{
"identification": {
"available": true
}
}
Working Hoursβ
Working Hours Structure
Structure:
{
"monday": {
"from": "08:00",
"to": "20:00"
},
"tuesday": {
"from": "08:00",
"to": "20:00"
},
"saturday": {
"from": "10:00",
"to": "15:00"
}
}
Notes:
- Missing days = location closed
- Closing time can be earlier than opening time (closes next day)
- Times in 24-hour format
HH:MM
Check if Open Now:
function isLocationOpenNow(location) {
if (!location.working_hours) return null;
const now = new Date();
const dayName = now.toLocaleDateString('en-US', { weekday: 'lowercase' });
const currentTime = now.toTimeString().slice(0, 5);
const hours = location.working_hours[dayName];
if (!hours) return false;
if (hours.from <= currentTime && currentTime <= hours.to) {
return true;
}
// Handle overnight closing
if (hours.to < hours.from) {
return currentTime >= hours.from || currentTime <= hours.to;
}
return false;
}
Location Categoriesβ
Get all available location categories (for pay service).
Request
GET /rest/v1/locations/pay-categories?locale={locale}
Response
[
{
"id": 1,
"title": "Entertainment"
},
{
"id": 2,
"title": "Bowling",
"parent_id": 1,
"images": {
"active_uri": "https://wallet.paysera.com/assets/locations/pay_category/bowling.png",
"inactive_uri": "https://wallet.paysera.com/assets/locations/pay_category/bowling_off.png"
}
}
]
View Category Fields & Example
Category Object
| Field | Type | Description |
|---|---|---|
id | integer | Category ID |
title | string | Category name |
parent_id | integer | Parent category ID (optional) |
images | object | Category icons (optional) |
Example: Filter Locations by Category
async function getLocationsByCategory(categoryId, lat, lng) {
// Get all nearby locations
const response = await api.getLocations({
lat, lng,
distance: 5000,
limit: 200
});
// Filter by category
return response.locations.filter(loc => {
const payService = loc.services?.pay;
return payService?.available &&
payService?.categories?.includes(categoryId);
});
}
// Find all restaurants nearby
const restaurants = await getLocationsByCategory(4, 54.68, 25.28);
Advanced Topicsβ
Pricing Information
Regular Price:
{
"title": "Cash-in fee",
"type": "price",
"price": {
"amount": 100,
"currency": "EUR",
"amount_decimal": "1.00"
}
}
Special Offer:
{
"title": "Cash-out is FREE!",
"type": "offer"
}
Display Pricing:
function formatPrices(location) {
if (!location.prices || location.prices.length === 0) {
return 'No pricing info available';
}
return location.prices.map(p => {
if (p.type === 'offer') {
return `β¨ ${p.title}`;
} else if (p.type === 'price') {
return `${p.title}: ${p.price.amount_decimal} ${p.price.currency}`;
}
}).join('\n');
}
Using location_id Parameter
Pass location_id when initiating transactions at specific locations.
When to Use:
- Payment at physical location
- Cash-in/cash-out operations
- Need location context in transaction
Benefits:
- Influences allowance usage
- Provides location info to user
- Better tracking and reporting
How to Pass:
MAC Authentication:
Authorization: MAC id="...", ..., ext="location_id=48&project_id=123"
SSL Certificate:
Wallet-Api-Location-Id: 48
Wallet-Api-Project-Id: 123
Example:
async function createLocationPayment(locationId, paymentData) {
return await api.createTransaction({
payments: paymentData,
headers: {
'Wallet-Api-Location-Id': locationId
}
});
}
Use Case 1: Location Finder App
class LocationFinder {
constructor(api) {
this.api = api;
this.categories = [];
this.locations = [];
}
async initialize() {
this.categories = await this.api.getLocationCategories('en');
}
async searchNearby(lat, lng, service = null, categoryId = null) {
const params = {
locale: 'en',
lat, lng,
distance: 5000,
limit: 100
};
const response = await this.api.getLocations(params);
let results = response.locations;
if (service) {
results = results.filter(loc =>
loc.services?.[service]?.available
);
}
if (categoryId && service === 'pay') {
results = results.filter(loc =>
loc.services?.pay?.categories?.includes(categoryId)
);
}
results.forEach(loc => {
loc.distance = this.calculateDistance(lat, lng, loc.lat, loc.lng);
});
results.sort((a, b) => a.distance - b.distance);
return results;
}
calculateDistance(lat1, lng1, lat2, lng2) {
// Haversine formula
const R = 6371e3;
const Ο1 = lat1 * Math.PI / 180;
const Ο2 = lat2 * Math.PI / 180;
const ΞΟ = (lat2 - lat1) * Math.PI / 180;
const ΞΞ» = (lng2 - lng1) * Math.PI / 180;
const a = Math.sin(ΞΟ/2) * Math.sin(ΞΟ/2) +
Math.cos(Ο1) * Math.cos(Ο2) *
Math.sin(ΞΞ»/2) * Math.sin(ΞΞ»/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return Math.round(R * c);
}
}
Use Case 2: Synchronize Locations
class LocationSync {
constructor(api, storage) {
this.api = api;
this.storage = storage;
}
async syncLocations() {
const lastSync = await this.storage.getLastSyncTime() || 0;
const response = await this.api.getLocations({
updated_after: lastSync,
status: 'active,inactive',
limit: 200
});
console.log(`Syncing ${response.locations.length} updated locations`);
for (const location of response.locations) {
if (location.status === 'active') {
await this.storage.upsertLocation(location);
} else {
await this.storage.deleteLocation(location.id);
}
}
await this.storage.setLastSyncTime(Date.now() / 1000);
return response.locations.length;
}
async syncCategories() {
const categories = await this.api.getLocationCategories('en');
await this.storage.saveCategories(categories);
return categories.length;
}
}
Best Practices
1. Cache Location Data
// Locations don't change frequently
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
class LocationCache {
async getLocations(params) {
const cacheKey = JSON.stringify(params);
const cached = await storage.get(cacheKey);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
const data = await api.getLocations(params);
await storage.set(cacheKey, {
data,
timestamp: Date.now()
});
return data;
}
}
2. Handle GPS Coordinates Properly
// Validate coordinates
function isValidCoordinate(lat, lng) {
return lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
}
// Get user location with error handling
async function getUserLocation() {
try {
const position = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
return {
lat: position.coords.latitude,
lng: position.coords.longitude
};
} catch (error) {
console.error('Location access denied');
return { lat: 54.687157, lng: 25.279652 }; // Vilnius fallback
}
}
3. Display Working Hours User-Friendly
function formatWorkingHours(hours) {
if (!hours) return 'Hours not available';
const days = ['monday', 'tuesday', 'wednesday', 'thursday',
'friday', 'saturday', 'sunday'];
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return days.map((day, i) => {
const h = hours[day];
if (!h) return `${dayNames[i]}: Closed`;
return `${dayNames[i]}: ${h.from} - ${h.to}`;
}).join('\n');
}
4. Use Incremental Sync
// Only fetch updated locations
async function incrementalSync() {
const lastSync = await storage.getLastSync() || 0;
const updated = await api.getLocations({
updated_after: lastSync,
status: 'active,inactive',
limit: 200
});
await storage.saveLocations(updated.locations);
await storage.setLastSync(Math.floor(Date.now() / 1000));
}
Common Issues
Issue: Too Many Results
Cause: No distance filtering
Solution:
// Always use distance filter
const locations = await api.getLocations({
lat, lng,
distance: 5000, // 5km radius
limit: 50
});
Issue: Stale Location Data
Cause: Not syncing updates
Solution:
// Regular sync with updated_after
async function regularSync() {
const lastSync = await storage.getLastSyncTime();
const updated = await api.getLocations({
updated_after: lastSync
});
// Process updates...
}
Issue: Wrong Distance Calculation
Cause: Using simple lat/lng diff
Solution:
// Use proper Haversine formula
function haversineDistance(lat1, lng1, lat2, lng2) {
const R = 6371e3;
const Ο1 = lat1 * Math.PI / 180;
const Ο2 = lat2 * Math.PI / 180;
const ΞΟ = (lat2 - lat1) * Math.PI / 180;
const ΞΞ» = (lng2 - lng1) * Math.PI / 180;
const a = Math.sin(ΞΟ/2) * Math.sin(ΞΟ/2) +
Math.cos(Ο1) * Math.cos(Ο2) *
Math.sin(ΞΞ»/2) * Math.sin(ΞΞ»/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return R * c;
}
Next Stepsβ
- Client Management - API client types and permissions
- Project Management - Working with projects
- Payments - Payment processing
- Authentication - Setup authentication