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
- Customer generates QR code: Customer requests a QR code through the API
- QR code displayed: Customer displays the QR code on their device
- Merchant scans code: Merchant scans the QR code to extract the token
- Payment processed: Merchant calls the API to process payment using the token
- 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
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)
The customer must have a valid payment method set as their default payment method in Stripe.
Request
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
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
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/..."
}
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
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
-
Token expiration: QR codes expire after 30 minutes by default. Adjust based on your use case.
-
HTTPS only: Always use HTTPS in production to protect tokens in transit.
-
Token storage: Don't store QR tokens in plain text. They're already encrypted in the QR code.
-
Revocation: Implement token revocation for lost/stolen devices.
-
Rate limiting: Consider implementing rate limiting on QR payment endpoints.
-
Payment method validation: Ensure customers have valid payment methods before generating QR codes.
Testing
To test the QR payment system:
- Create a customer with a payment method
- Generate a QR code for the customer
- Scan the QR code to extract the token
- Process a payment using the token
- Verify the charge was created and receipt was sent