QR Coupon
Developer docs
QR Coupon is built to be extended. It uses WooCommerce’s own cart and coupon APIs, exposes a read-only REST namespace, and fires actions and filters at every meaningful step. There are no external services — QR codes render client-side, and the only network dependency is Freemius for licensing. This page covers the public surface for v1.0.0.
The scan URL
Every QR encodes a plain URL on your store. When scanned, it’s intercepted on template_redirect, the product (or variation) is added to the cart, the coupon is applied, and the customer lands on the cart — already discounted.
https://your-store.com/?qrcw=1&p={product_id}[&v={variation_id}][&c={coupon_code}]
p— product ID (required).v— variation ID (optional; for a specific size/colour).c— coupon code (optional; omit for a plain add-to-cart).
REST API
Namespace qrcw/v1, read-only. Both routes require the edit_products capability. Writes are handled by admin-AJAX, not REST. Pro adds no REST endpoints — its analytics, campaign and conversion data live in the admin (AJAX + admin pages), so the two routes below are the complete REST surface.
| Endpoint | Method | Purpose |
|---|---|---|
/wp-json/qrcw/v1/url | GET | Build a scan URL from product_id (plus optional coupon, variation_id). |
/wp-json/qrcw/v1/products/{id} | GET | List saved QR records for a product — scan counts and image URLs included. |
Action hooks
Hooks marked Pro only fire when the Pro add-on is active — everything else ships in the free plugin.
| Hook | Fires when | Arguments |
|---|---|---|
qrcw_cart_tagged | a scan tags the cart | $attachment_id, $product_id, $coupon_code, $scan_id |
qrcw_scan_recorded | a scan row is written | $args, $scan_id |
qrcw_conversion_linked Pro | a scan is linked to an order | $scan_id, $order_id |
qrcw_conversion_updated Pro | a linked order changes | $order_id |
qrcw_delete_qr | a QR is removed | $product_id, $attachment_id |
qrcw_product_deleted | a product is removed | $product_id |
qrcw_campaign_deleted Pro | a campaign is deleted | $campaign_id, $delete_codes |
qrcw_all_campaigns_deleted Pro | every campaign is bulk-deleted | $delete_codes |
qrcw_purge_data | all plugin data is purged | — |
qrcw_register_submenus | the admin menu is built (Pro adds menus) | — |
qrcw_fs_loaded | Freemius has finished loading (safe to call qrcw_fs()) | — |
Filter hooks
| Hook | Changes | Arguments |
|---|---|---|
qrcw_intercept_scan | return true to claim a scan and skip the default add-to-cart flow | false |
qrcw_client_ip | override the resolved IP before it’s hashed | $raw_ip |
qrcw_settings_defaults | default settings | $defaults |
qrcw_sanitize_settings | sanitise settings on save | $clean, $input |
qrcw_sanitize_qr_design | sanitise a QR design payload | array(), $design_raw |
qrcw_get_product_codes | provide saved codes for a product | array(), $product_id |
qrcw_admin_js_data | the admin JS localisation payload | $data |
qrcw_codes_table_columns / _records / _column_html | extend the All QR Codes table | columns / records / '', $item, $column |
qrcw_clean_orphan_media / qrcw_clean_deleted_entities | row count from the data-cleanup tools | $count |
Example — append a column to the All QR Codes admin table:
add_filter( 'qrcw_codes_table_columns', function ( $columns ) {
$columns['campaign'] = __( 'Campaign', 'your-textdomain' );
return $columns;
} );
Admin AJAX & JavaScript
- Admin AJAX: ~22
wp_ajax_qrcw_*actions handle all writes (generation, saving, deletion). They’re nonce-protected withqrcw_admin(the purge action usesqrcw_purge). - JS globals:
window.qrcwRenderbuilds the QR config and renders to canvas;qrcwRender.readPreviewDesign()reads the live design;confirmDialog()drives confirmations. - No shortcodes, no WP-CLI — the public surface is REST (read) + admin-AJAX (write).
Data & privacy
QR Coupon stores its data in four custom tables — qrcw_codes, qrcw_scans, qrcw_campaigns, and (Pro) qrcw_conversions — dropped on uninstall. It’s privacy-respecting by design:
- QR codes are generated in the browser — nothing is sent to a third-party service.
- Scan IPs are stored as an HMAC-SHA256 hash, never in the clear (override the source IP with
qrcw_client_ip). - It implements the WordPress Privacy API exporters and erasers; conversions are anonymised to preserve aggregate revenue figures.
Building an integration and need a hook that isn’t here? These are the stable, documented entry points for v1.0.0 — open an issue on the plugin’s WordPress.org support forum and we’ll point you at the right one.
