QR Payments

The QR Payments system allows merchants to scan customer QR codes to process payments. Customers generate secure, time-limited QR codes that merchants can scan to charge their default payment method.

Overview

QR Payments enable:

  • Contactless payments: Customers display QR codes, merchants scan them
  • Secure transactions: JWT-based tokens with cryptographic signatures
  • Time-limited codes: QR codes expire after a configurable time (default: 30 minutes)
  • Automatic processing: Uses customer's default payment method from Stripe
  • Receipt delivery: Automatically sends receipts to customers via email

How it works

  1. Customer generates QR code: Customer requests a QR code through the API
  2. QR code displayed: Customer displays the QR code on their device
  3. Merchant scans code: Merchant scans the QR code to extract the token
  4. Payment processed: Merchant calls the API to process payment using the token
  5. Receipt sent: Customer automatically receives a receipt via email

Security features

  • Name
    JWT Tokens
    Type
    string
    Description

    QR codes contain JWT tokens signed with HMAC-SHA256

  • Name
    Token Expiration
    Type
    string
    Description

    Tokens automatically expire after a set time (default: 30 minutes)

  • Name
    Account Validation
    Type
    string
    Description

    Tokens are validated against the account ID

  • Name
    Revocation Support
    Type
    string
    Description

    Tokens can be revoked if compromised

  • Name
    Rate Limiting
    Type
    string
    Description

    Can be added via API key rate limiting


POST/customers/:id/qr

Generate customer QR code

Generate a QR code for a customer. The QR code contains a secure JWT token that merchants can use to process payments.

Required Parameters

  • Name
    customer_id
    Type
    string
    Description

    The customer ID (in URL path)

Optional Parameters

  • Name
    expires_in_minutes
    Type
    integer
    Description

    Minutes until QR code expires (default: 30)

Request

POST
/customers/{customer_id}/qr
curl -X POST https://api.cari.finance/customers/cus_abc123/qr \
  -H "Authorization: Bearer pk_test_27436257e3fe4b0fa266f4a6f59047a3" \
  -H "Content-Type: application/json" \
  -d '{
    "expires_in_minutes": 30
  }'

Response

{
  "qr_code": "data:image/png;base64,iVBORw0KG...",
  "qr_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": 1702310400,
  "customer_id": "cus_abc123..."
}

Response fields

  • Name
    qr_code
    Type
    string
    Description

    Base64-encoded PNG image of the QR code (data URI format)

  • Name
    qr_token
    Type
    string
    Description

    JWT token extracted from the QR code

  • Name
    expires_at
    Type
    integer
    Description

    Unix timestamp when the QR code expires

  • Name
    customer_id
    Type
    string
    Description

    The customer ID


POST/qr-payments/process

Process QR payment

Process a payment using a scanned QR code token. The payment will be charged to the customer's default payment method.

Required Parameters

  • Name
    qr_token
    Type
    string
    Description

    JWT token extracted from the scanned QR code

  • Name
    amount
    Type
    integer
    Description

    Amount to charge in cents (e.g., 1000 = $10.00)

Optional Parameters

  • Name
    currency
    Type
    string
    Description

    Three-letter ISO currency code (default: "usd")

  • Name
    description
    Type
    string
    Description

    Description of the payment

  • Name
    merchant_id
    Type
    string
    Description

    Merchant ID for marketplace scenarios

Request

POST
/qr-payments/process
curl -X POST https://api.cari.finance/qr-payments/process \
  -H "Authorization: Bearer pk_test_27436257e3fe4b0fa266f4a6f59047a3" \
  -H "Content-Type: application/json" \
  -d '{
    "qr_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "amount": 1000,
    "currency": "usd",
    "description": "Payment at Cari Market",
    "merchant_id": "merchant_123"
  }'

Response

{
  "charge_id": "ch_abc123...",
  "customer_id": "cus_abc123...",
  "amount": 1000,
  "currency": "usd",
  "status": "succeeded",
  "receipt_url": "https://pay.stripe.com/receipts/..."
}

POST/qr-payments/revoke

Revoke QR token

Revoke a QR token to prevent unauthorized use. Useful if a device is lost or stolen, or if a QR code has been compromised.

Required Parameters

  • Name
    qr_token
    Type
    string
    Description

    The QR token to revoke

Request

POST
/qr-payments/revoke
curl -X POST https://api.cari.finance/qr-payments/revoke \
  -H "Authorization: Bearer pk_test_27436257e3fe4b0fa266f4a6f59047a3" \
  -H "Content-Type: application/json" \
  -d '{
    "qr_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'

Response

{
  "success": true,
  "message": "QR token revoked successfully"
}

Integration examples

Customer side (Cari Wallet)

// Generate QR code for customer
const response = await fetch("https://api.cari.finance/customers/cus_123/qr", {
   method: "POST",
   headers: {
      Authorization: `Bearer ${customerApiKey}`,
      "Content-Type": "application/json",
   },
   body: JSON.stringify({
      expires_in_minutes: 30,
   }),
});

const { qr_code, qr_token, expires_at } = await response.json();

// Display QR code image
document.getElementById("qr-code").src = qr_code;

Merchant side (Cari Market)

// Scan QR code and extract token
const qrToken = scannedQRCodeData; // From QR scanner

// Process payment
const response = await fetch("https://api.cari.finance/qr-payments/process", {
   method: "POST",
   headers: {
      Authorization: `Bearer ${merchantApiKey}`,
      "Content-Type": "application/json",
   },
   body: JSON.stringify({
      qr_token: qrToken,
      amount: 1000, // $10.00
      currency: "usd",
      description: "Purchase at Cari Market",
      merchant_id: "merchant_123",
   }),
});

const { charge_id, status, receipt_url } = await response.json();

if (status === "succeeded") {
   console.log("Payment successful! Charge ID:", charge_id);
}

JWT token structure

QR tokens are JWT tokens with the following claims:

{
   "sub": "cus_abc123...",
   "iat": 1702308000,
   "exp": 1702310400,
   "jti": "uuid-here",
   "account_id": "acct_123..."
}
  • Name
    sub
    Type
    string
    Description

    Customer ID (subject)

  • Name
    iat
    Type
    integer
    Description

    Issued at (Unix timestamp)

  • Name
    exp
    Type
    integer
    Description

    Expiration (Unix timestamp)

  • Name
    jti
    Type
    string
    Description

    Token ID (for revocation)

  • Name
    account_id
    Type
    string
    Description

    Account ID (for validation)


Error handling

Common errors you may encounter:

  • Name
    400 Bad Request
    Type
    string
    Description

    Invalid QR token or missing required fields

  • Name
    401 Unauthorized
    Type
    string
    Description

    Invalid or missing API key

  • Name
    404 Not Found
    Type
    string
    Description

    Customer not found or QR token not found

  • Name
    422 Unprocessable Entity
    Type
    string
    Description

    Customer doesn't have a payment method or token has expired


Security best practices

  1. Token expiration: QR codes expire after 30 minutes by default. Adjust based on your use case.

  2. HTTPS only: Always use HTTPS in production to protect tokens in transit.

  3. Token storage: Don't store QR tokens in plain text. They're already encrypted in the QR code.

  4. Revocation: Implement token revocation for lost/stolen devices.

  5. Rate limiting: Consider implementing rate limiting on QR payment endpoints.

  6. Payment method validation: Ensure customers have valid payment methods before generating QR codes.


Testing

To test the QR payment system:

  1. Create a customer with a payment method
  2. Generate a QR code for the customer
  3. Scan the QR code to extract the token
  4. Process a payment using the token
  5. Verify the charge was created and receipt was sent

Was this page helpful?