Skip to main content

QR Code Developer Guide

Overview

The QR Code feature is tightly integrated with the marker system. Each marker gets a unique short code that is encoded into a QR code. When scanned, the QR code directs users to a deep link URL that retrieves the marker's full details from the public API.

Architecture

QR Code Architecture


Short Code Generation

Algorithm

Each marker is assigned a unique 8-character alphanumeric short code stored in the short_code field.

Character Set: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ

Implementation (internal/util/shortcode.go):

const charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

func GenerateShortCode() string {
b := make([]byte, 8)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}

Uniqueness Guarantee

  • Short codes are generated using a cryptographically secure random number generator
  • Database enforces uniqueness via UNIQUE constraint on short_code column
  • Collision probability is extremely low (~1 in 2.8 trillion for 8 characters with 36-character set)

QR Code Generation Flow

Endpoint

GET /v1/markers/{id}/qr

Authentication: Required (Bearer token)

Response: PNG image file

Implementation Details

Handler (internal/handler/marker.go):

func (h *MarkerHandler) GetQRCode(c *gin.Context) {
id := c.Param("id")

// Fetch marker with short code
marker, err := h.markerService.GetByID(id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "marker not found"})
return
}

// Create deep link URL
deepLink := fmt.Sprintf("%s/marker/%s", h.config.DeepLinkBaseURL, marker.ShortCode)

// Generate QR code with logo
qr, err := qrcode.NewWithLogo(deepLink, h.config.LogoPath, &qrcode.Config{
Width: 300,
LogoWidth: 90, // 3x multiplier
LogoPadding: 10,
})

// Return PNG image
c.Header("Content-Type", "image/png")
c.Header("Content-Disposition", fmt.Sprintf(`inline; filename="%s.png"`, marker.ShortCode))
c.Data(http.StatusOK, "image/png", qr)
}

Library Used: github.com/yeqown/go-qrcode/v2

{DEEP_LINK_BASE_URL}/marker/{shortCode}

Example:

https://bamboomapper.com/marker/ABC12345

Public Endpoint

GET /v1/markers/code/{shortCode}

Authentication: Not required (public endpoint)

Response: JSON with full marker details

Flow

  1. User scans QR code with camera app
  2. Camera app detects URL in QR code
  3. User is redirected to deep link URL
  4. Mobile app handles deep link (or browser opens web page)
  5. App makes request to public endpoint with short code
  6. Server returns marker details
  7. App displays information to user

Response Example

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"short_code": "ABC12345",
"name": "Bambu Apus",
"strain": "Gigantochloa apus",
"quantity": 15,
"latitude": -7.12345678,
"longitude": 109.12345678,
"image_url": "https://cdn.bamboomapper.com/markers/abc12345.jpg",
"owner_name": "Budi Santoso",
"owner_contact": "+6281234567890",
"description": "Bambu apus berkualitas baik",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}

Configuration

Environment Variables

VariableDescriptionExample
DEEP_LINK_BASE_URLBase URL for deep linkshttps://bamboomapper.com

QR Code Options

OptionValueDescription
FormatPNGImage format
Width300pxQR code size
LogoSapuran logoCentered logo
Logo Width90px3x size multiplier
Logo Padding10pxSpace around logo

API Reference

Generate QR Code

Request:

curl -X GET "https://api.bamboomapper.com/v1/markers/{id}/qr" \
-H "Authorization: Bearer YOUR_TOKEN" \
--output qr-code.png

Response:

  • Status: 200 OK
  • Content-Type: image/png
  • Body: Binary PNG image

Error Responses:

StatusDescription
401Unauthorized (invalid/missing token)
404Marker not found

Get Marker by Short Code

Request:

curl -X GET "https://api.bamboomapper.com/v1/markers/code/{shortCode}"

Response:

{
"id": "...",
"short_code": "ABC12345",
"name": "Bambu Apus",
...
}

Error Responses:

StatusDescription
404Marker with given short code not found

Database Schema

CREATE TABLE markers (
id UUID PRIMARY KEY,
short_code VARCHAR(8) UNIQUE NOT NULL, -- QR code target
creator_id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
strain VARCHAR(100),
quantity INT,
latitude DECIMAL(10, 8) NOT NULL,
longitude DECIMAL(11, 8) NOT NULL,
image_url TEXT,
owner_name VARCHAR(100),
owner_contact VARCHAR(50),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);

Security Considerations

  1. QR Code Generation requires authentication (user must own the marker)
  2. Public Endpoint is intentionally unauthenticated to allow anyone to scan and view marker info
  3. Short Codes are randomly generated and not guessable for practical purposes
  4. CORS configuration controls which domains can access the public endpoint