paynow_qr
Generate EMVCo-compliant Singapore PayNow (SGQR) QR payloads in pure Ruby.
Zero runtime dependencies — pair with any QR renderer (rqrcode, chunky_png,
or your platform of choice).
Why
Ruby was the only major ecosystem missing a clean PayNow library. Every other
runtime has one (paynowqr on npm, pyPayNowSg on PyPI, etc.). This is the
Ruby port.
Install
# Gemfile
gem "paynow_qr"
bundle install
# or
gem install paynow_qr
Quick start
require "paynow_qr"
payload = PaynowQR.generate(
proxy_type: :uen,
proxy_value: "201234567A",
amount: 25.00,
company: "ACME PTE LTD",
reference: "INV-001"
)
# => "00020101021126..."
Render it as an image with your favourite QR gem:
require "rqrcode"
png = RQRCode::QRCode.new(payload).as_png(size: 512)
File.binwrite("paynow.png", png.to_s)
API
PaynowQR.generate(**opts) / PaynowQR::Generator.new(**opts).payload
| Option | Type | Default | Description |
|---|---|---|---|
proxy_type |
:uen, :mobile, or "0".."9" |
required | PayNow proxy type. |
proxy_value |
String |
required | UEN (e.g. "201234567A") or mobile with country code ("+6591234567"). |
amount |
Numeric, String, nil |
nil |
SGD amount. Omit for an any-amount QR. |
editable |
Boolean |
false |
If true, the QR is dynamic — the amount is omitted from the payload and entered at payment time. |
company |
String |
"NA" |
Merchant name (max 25 chars per EMVCo). |
expiry |
Date, Time, String, nil |
today + 1 year | Expiry as a Date/Time, or "YYYYMMDD" string. |
reference |
String, nil |
nil |
Optional bill / invoice reference. |
Returns the EMVCo payload String. Pass it to any QR renderer.
Proxy types
| Symbol | Code | Notes |
|---|---|---|
:mobile |
0 |
Include country code (+65…). |
:uen |
2 |
Standard Singapore UEN. |
You can pass a single-digit string (e.g. "3") if you need a proxy type not
covered by the symbols above — this library does not restrict future codes.
Examples
Fixed-amount UEN QR
PaynowQR.generate(
proxy_type: :uen,
proxy_value: "201234567A",
amount: 49.90,
company: "ACME PTE LTD"
)
Any-amount mobile QR
PaynowQR.generate(
proxy_type: :mobile,
proxy_value: "+6591234567",
company: "Jane Doe"
)
Editable-amount QR with expiry
PaynowQR.generate(
proxy_type: :uen,
proxy_value: "201234567A",
amount: 100.00,
editable: true,
expiry: Date.new(2026, 12, 31),
reference: "ORDER-42"
)
Specification
This library implements the EMVCo Merchant Presented Mode QR Code specification and the MAS SGQR / PayNow specification.
Field layout:
| ID | Name | Notes |
|---|---|---|
| 00 | Payload Format Indicator | Always 01 |
| 01 | Point of Initiation Method | 11 static (fixed amount), 12 dynamic |
| 26 | Merchant Account Info (PayNow) | See sub-fields below |
| 52 | Merchant Category Code | 0000 (unused by PayNow) |
| 53 | Currency | 702 (SGD) |
| 54 | Transaction Amount | Omitted for dynamic QRs |
| 58 | Country Code | SG |
| 59 | Merchant Name | |
| 60 | Merchant City | Singapore |
| 62 | Additional Data | Contains sub-field 01 (reference) |
| 63 | CRC | CRC-16/CCITT-FALSE |
Sub-fields under 26:
| ID | Name | Value |
|---|---|---|
| 00 | AID | SG.PAYNOW |
| 01 | Proxy Type | 0 (mobile) / 2 (UEN) |
| 02 | Proxy Value | UEN or mobile |
| 03 | Editable Flag | 0 (fixed) / 1 (editable) |
| 04 | Expiry | YYYYMMDD |
Development
bin/setup
bundle exec rspec
bundle exec rubocop
Install locally:
bundle exec rake install
Contributing
Bug reports and PRs are welcome at https://github.com/ryzs/paynow_qr.
Before opening a PR please run bundle exec rake and add tests.
License
Released under the MIT License.