Skip to main content

Scan and Pay

Paysera offers a secure API that lets customers easily pay for products or services using QR codes. This guide covers static QR code implementation for POS/restaurant scenarios.

QR Code Payments

Enable contactless payments with static or dynamic QR codes at your physical locations.

Use Case: Restaurant/Cafe Payment​

Customer scans a QR code at their table, reviews the order, and pays instantly from their Paysera app without waiting for a waiter.

QR Code Format: PAYSERA*{locationId}:{identifier}

Example: PAYSERA*9975:table1


Payment Flow​


QR Code Types​

1. Static Location QR

Format: PAYSERA*{locationId}:{identifier}

Use for: Restaurant tables, hotel rooms, parking spots, service desks

Example: PAYSERA*9975:table12

2. Transaction QR

Format: PAYSERA%{transactionKey}

Use for: One-time payments, invoice payments, quick checkouts

Example: PAYSERA%ATaNL6X6CivO2Ou0FMPt

3. Wallet QR

Format: EVP{walletId} (padded to 12 digits)

Use for: Direct wallet payments, tipping, donations

Example: EVP000000014471


Implementation Steps​

  1. Generate QR Codes - Create static QR codes for locations
  2. Monitor Spots - Poll for spots with "waiting" status: GET /rest/v1/spots?status=waiting
  3. Create Transaction - When spot is waiting, create transaction for order
  4. Create Order - Link transaction to the spot
  5. Wait for Confirmation - Customer sees order and confirms payment
  6. Confirm Transaction - Confirm the reserved transaction
  7. Close Spot - Close spot after payment is complete

Implementation Examples​

<?php
require_once 'vendor/autoload.php';

use Paysera\WalletApi\ClientFactory;

class ScanAndPayHandler
{
private $client;
private $locationId;

public function __construct($clientId, $secret, $locationId)
{
$factory = ClientFactory::create([
'auth' => [
'mac' => [
'mac_id' => $clientId,
'mac_secret' => $secret,
],
],
]);

$this->client = $factory->getWalletClient();
$this->locationId = $locationId;
}

public function checkWaitingSpots()
{
$spots = $this->client->getSpots([
'status' => 'waiting',
'location_id' => $this->locationId,
]);

return $spots;
}

public function createOrderTransaction($orderDetails)
{
$transaction = $this->client->createTransaction([
'payments' => [[
'description' => $orderDetails['description'],
'price' => $orderDetails['total'],
'currency' => 'EUR',
'parameters' => [
'order_id' => $orderDetails['order_id'],
'table' => $orderDetails['table'],
],
]],
]);

return $transaction;
}

public function createSpotOrder($spotId, $transactionKey)
{
$order = $this->client->createSpotOrder($spotId, [
'transaction_key' => $transactionKey,
]);

return $order;
}

public function getReservedTransactions()
{
$transactions = $this->client->getTransactions([
'status' => 'reserved',
'project_id' => $this->locationId,
]);

return $transactions;
}

public function confirmTransaction($transactionKey)
{
return $this->client->confirmTransaction($transactionKey);
}

public function closeSpot($spotId)
{
return $this->client->closeSpot($spotId);
}
}

// Usage Example
$handler = new ScanAndPayHandler(
'wkVd93h2uS',
'IrdTc8uQodU7PRpLzzLTW6wqZAO6tAMU',
9975
);

// Poll for waiting spots
$waitingSpots = $handler->checkWaitingSpots();

foreach ($waitingSpots as $spot) {
$orderDetails = [
'order_id' => 1234,
'table' => $spot->getIdentifier(),
'description' => 'Order #1234',
'total' => 2599,
];

$transaction = $handler->createOrderTransaction($orderDetails);
$order = $handler->createSpotOrder($spot->getId(), $transaction->getKey());
}

// Check for reserved transactions
$reservedTransactions = $handler->getReservedTransactions();

foreach ($reservedTransactions as $transaction) {
$handler->confirmTransaction($transaction->getKey());
}

Advanced Topics​

Troubleshooting

Spot stuck in "waiting"

  • Close the spot manually
  • Check if transaction was created
  • Verify spot_id is correct

Transaction not found

  • Ensure transaction was created successfully
  • Check transaction_key is correct
  • Verify project_id matches

QR code not working

  • Verify QR format: PAYSERA*{locationId}:{identifier}
  • Check location_id is registered
  • Ensure identifier is unique per location
Testing
Production Testing

Wallet API does not have a sandbox environment. All testing must be done in production with real transactions.

Testing Strategy:

  1. Use small amounts - Test with minimal values (1-2 EUR)
  2. Test location - Set up dedicated test QR codes
  3. Monitor carefully - Watch all test transactions
  4. Test scenarios:
    • Generate test QR codes
    • Scan with Paysera app
    • Verify spot appears in waiting status
    • Create and confirm test transaction
    • Close spot after completion

Resources​


Next Steps​

  • Implement spot monitoring service
  • Set up automatic transaction confirmation
  • Add order management system
  • Integrate with your POS
  • Deploy to production
Production Deployment

Use a dedicated worker process to monitor spots continuously. Don't rely on web requests.