<!-- 1. Attribute — simplest -->
<div data-wf-qr
data-wf-content="https://example.com"
data-wf-size="200"
data-wf-label="Scan me">
</div>
<!-- 2. Inline JSON — matches wf-chart pattern -->
<div data-wf-qr data-wf-size="200" data-wf-ec-level="H">
<script type="application/json">
{ "content": "bitcoin:address?amount=0.001&label=Order+123" }
</script>
</div>
QR Code
Pure client-side QR code generation — no external libraries. Add data-wf-qr to any element and supply content via a data-wf-content attribute, an inline <script type="application/json"> block, or a remote fetch via data-wf-src. The component builds the QR matrix and renders a single-path SVG fully in the browser — theme-aware, instantly downloadable. Supports byte mode, versions 1–10 (≈ 174 chars at EC level M).
Inline Content
The simplest init: set data-wf-content to the text or URL to encode. A label and custom size are optional.
Remote Fetch
Use data-wf-src to fetch content from the server. The component shows a skeleton loader while waiting, then renders the QR from the content key in the JSON response. Optional data-wf-params appends query parameters to the request.
<div data-wf-qr
data-wf-src="/api/payment/qr"
data-wf-params='{"order_id":"<?php echo $order_id; ?>"}'
data-wf-size="200"
data-wf-label="Scan with your wallet">
</div>
// Required: a "content" key with the string to encode
{ "content": "bitcoin:address?amount=0.001&label=Order+456" }
// The component also accepts the raw string as the entire response body
"bitcoin:address?amount=0.001"
case 'getPaymentQr':
$orderId = trim($_GET['order_id'] ?? '');
$order = Order::find($orderId);
if (!$order) json_out(['type' => 'negative', 'message' => 'Order not found.'], 404);
// Build a Bitcoin URI or any other content string
$uri = 'bitcoin:' . $order->wallet_address
. '?amount=' . $order->amount_btc
. '&label=' . rawurlencode('Order #' . $orderId);
json_out(['content' => $uri]);
Ajax Action Bridge
Add data-wf-qr-target="#el" to any wf-ajax-action trigger. When the action succeeds and the server returns a content key, the component re-renders automatically — no JavaScript required. The example below lets the user switch blockchain network and instantly updates the wallet address QR.
Selecting a network calls the server and updates the QR code with the matching wallet address.
<!-- QR target receives updates when server returns { content: "…" } -->
<div id="pay-qr" data-wf-qr data-wf-size="200"
data-wf-content="bitcoin:1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2">
</div>
<select data-wf-ajax-action="getAddress"
data-wf-url="/api/wallets"
data-wf-qr-target="#pay-qr">
<option value="btc">Bitcoin (BTC)</option>
<option value="eth">Ethereum (ETH)</option>
<option value="usdc">USDC (Polygon)</option>
</select>
case 'getAddress':
$chain = trim($_REQUEST['value'] ?? 'btc');
$wallets = [
'btc' => 'bitcoin:1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
'eth' => 'ethereum:0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
'usdc' => 'polygon:0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
];
json_out(['content' => $wallets[$chain] ?? $wallets['btc']]);
Crypto Payment Integration
Pair data-wf-qr with data-wf-poll for a complete payment flow: the QR code shows the wallet address while the poll component monitors the transaction status and redirects on confirmation.
<!--
Complete crypto payment page.
Both components share the same order_id.
wf-qr fetches the wallet address from the server.
wf-poll monitors payment status and redirects on confirmation.
-->
<div data-wf-qr
data-wf-src="/api/payment/qr"
data-wf-params='{"order_id":"<?php echo $order_id; ?>"}'
data-wf-size="220"
data-wf-label="Scan with your wallet"
id="pay-qr">
</div>
<div data-wf-poll
data-wf-url="/api/payment/status"
data-wf-params='{"order_id":"<?php echo $order_id; ?>"}'
data-wf-interval="3s"
data-wf-timeout="30m"
data-wf-stop-on="confirmed,expired"
data-wf-redirect-on="confirmed"
data-wf-redirect-url="/membership"
data-wf-redirect-delay="3s"
data-wf-parent="#pay-status"
data-wf-timer="#pay-countdown">
</div>
<div id="pay-status">Waiting for payment…</div>
<span id="pay-countdown">30:00</span>
Data Attributes
| Attribute | Default | Description |
|---|---|---|
data-wf-qr |
— | Required. Presence marker. No value needed. |
data-wf-content="text" |
empty | Text or URL to encode. Simplest init; can also be supplied via inline JSON or
data-wf-src. |
data-wf-size="200" |
200 |
Rendered SVG width and height in px. The quiet-zone modules are included within this dimension. |
data-wf-ec-level="M" |
M |
Error correction level: L (7%) | M (15%) | Q (25%) |
H (30%). Higher levels increase QR size but add recovery capability. |
data-wf-color="#000" |
CSS --wf-foreground |
Dark module color. Inherits from the --wf-foreground CSS variable when empty, so
dark-mode is automatic. |
data-wf-background="" |
transparent | Light module color. Transparent by default so the QR blends into any card background. |
data-wf-margin="4" |
4 |
Quiet zone width in modules. The QR standard recommends a minimum of 4. |
data-wf-label="text" |
empty | Optional caption rendered below the QR image. |
data-wf-label-size="13" |
13 |
Caption font-size in px. |
data-wf-src="/url" |
empty | GET-fetch URL. Component shows a skeleton loader while fetching, then renders from
{ "content": "…" } in the response. |
data-wf-params='{"k":"v"}' |
empty | JSON object appended as query parameters to the data-wf-src URL. |
data-wf-csrf-header="X-CSRF-Token" |
X-CSRF-Token |
Header name for the CSRF token, read from <meta name="csrf-token">. Set to
"" to disable. |
Events & API
| Event | When | detail |
|---|---|---|
wf:qr-ready |
After first render (attribute, inline JSON, or remote fetch) | { content, size } |
wf:qr-update |
After render() re-draw (e.g. from ajax-action bridge) |
{ content, size } |
wf:qr-error |
Content too long for versions 1–10 or invalid input | { content, error } |
const qr = document.querySelector('#pay-qr')._wfQr
// Re-encode and redraw with new content
qr.render('https://example.com/updated')
// Download the current QR as an SVG file
qr.download('payment-qr.svg')
// Get the raw SVG markup string (for embedding or sending to a server)
const svg = qr.toSVG()
// Full teardown
qr.destroy()
document.querySelector('#pay-qr').addEventListener('wf:qr-ready', e => {
console.log('QR ready:', e.detail.content)
})
document.querySelector('#pay-qr').addEventListener('wf:qr-error', e => {
console.warn('QR failed:', e.detail.error.message)
})