What Is a QR Ph Code? How to Generate, Scan and Verify One in GCash, Maya and Bank Apps (2026)
I run payments and integration for a company that onboards foreign merchants and developers in the Philippines, and "QR Ph code" is a search that reaches my desk from three directions. A newly arrived expat wants to know what the square on every counter is and whether their phone can pay it. A freelancer wants to make one so clients can pay them. A developer wants one on a checkout page per order. This article answers all three: what the code is and what is encoded in it, how an individual generates a personal code in GCash, Maya or a bank app, how to scan and verify one before confirming, when a business needs a merchant code instead, and what the per-order flow looks like in code. Fees and limits change often and are left out; read them in your app when you transact. For why one code works across every wallet, see QRPH interoperability explained; this page is about the code itself.
What a QR Ph code is, and what it is not
QR Ph is the Philippines' national QR code standard. The Bangko Sentral ng Pilipinas established it under Circular No. 1055 in 2019 within the National Retail Payment System, and it is built on the EMVCo QR specification. The first use case was person-to-person payments in 2019; person-to-merchant payments followed in 2021. In February 2023, BSP Memorandum No. M-2023-005 required BSP-supervised payment service providers to use and display QR Ph on their internet and mobile channels from 1 July 2023 and to disable proprietary payment QR codes from that date. That is why the GCash code at a sari-sari store, the Maya code on a cafe's terminal and the code your bank app generates are now the same kind of object.
Payments against a QR Ph code are cleared through InstaPay, the real-time retail payment rail connecting participating banks and e-money issuers, so a code generated in one app can be paid from any other participating app. The rail-level comparison with other transfer methods is in QRPH vs InstaPay vs PESONet.
What a QR Ph code is not: an app, a wallet or a card. It carries no money and cannot pull money from whoever scans it. It is a structured text string, rendered as a QR image, that tells a paying app which participant and account should receive a payment, under what registered name, and optionally for what amount and reference. Authentication and settlement happen in the apps and on the rail, not in the code.
What is encoded inside a QR Ph code
Scan a QR Ph code with a generic QR reader and you will see a long string of digits and letters. It follows the EMVCo tag-length-value layout: each field is a two-digit tag, a two-digit length, then the value, in an order fixed by the standard. That fixed order is what lets any participating app parse any participant's code.
| Field | What it carries | Why it matters to a payer or a developer |
|---|---|---|
| Payload format indicator | Marks the string as an EMVCo payment payload | First sanity check; apps reject anything without it |
| Point of initiation | Static (reusable) or dynamic (one payment) | Tells the app whether to ask for an amount or lock it |
| Merchant account information | The participant and the account identifier at that participant | Where the money goes; issued by the participant, not typed by anyone |
| Merchant category code | The business category, or a generic value for personal codes | Used for risk and reporting on the acquiring side |
| Transaction currency | The ISO numeric code for the peso, 608 | QR Ph payments are peso payments |
| Transaction amount | Present in dynamic codes, absent in static ones | If present, the payer cannot change it |
| Country code | PH | Identifies the scheme |
| Merchant or recipient name | The account holder's registered name | What the paying app shows before confirmation; your verification anchor |
| City | The registered city | Shown by some apps; a mismatch with the shop is worth a question |
| Additional data | Optional reference, bill or order number | How a merchant matches a payment to an order without the payer typing anything |
| CRC | A checksum over the whole payload | A damaged print or an edited string fails here and will not scan |
Two consequences. You can render a payload with any QR library, because the image is only a carrier for the string; you cannot originate the account identifiers, which come from a participant. And the name field is the only part a human can check, which is why the paying app surfaces it before confirmation.
How to generate a personal QR Ph code in GCash, Maya or a bank app
Every participating wallet and most participating bank apps let an account holder generate a personal, person-to-person code. Menu labels differ and change, so I describe the steps generically.
- Open the QR or receive-money section. The app builds a static code from your registered name and your account identifier at that participant.
- Optionally enter an amount. Most apps then produce a one-time code with the amount embedded, which makes it a dynamic code for that single payment.
- Show the code on screen, or save and share the image. The payer scans it from their own wallet or bank app, which does not have to be the same brand as yours.
- Watch for the credit in your transaction history. The payer sees your registered name before confirming, so the name on your account is the name they will see.
Anyone with a verified account at a participant can do this. For expats and long-stay visitors the hurdle is the account, not the code: you need a Philippine mobile number registered in your name and identity documents the wallet or bank accepts. Freelancers and small sellers often stop here and treat the personal code as a business code. It works for occasional receiving, but payers see your personal name rather than a trade name, there is no order reference unless the payer types one, and settlement lands in a personal account under personal terms. The later sections explain when that stops being enough.
How to scan and pay, and how to verify before you confirm
Paying is the mirror image. Open your wallet or bank app, choose scan, point the camera at the code or upload a saved image. The app parses the payload, resolves the account through the rail, and shows a confirmation screen with the recipient's registered name and, for a dynamic code, the amount; for a static code you type the amount. You confirm with your MPIN or biometrics and both apps record the transaction.
That confirmation screen is the verification step. My checklist for new staff and for every traveler who asks:
- Match the name to the counter. A restaurant's code should resolve to the restaurant's registered or trading name. If it resolves to an individual you have never heard of, stop and ask. Staff sometimes present their own personal code; that is also exactly what a fraudster's overlay looks like.
- Look at the physical code. The common fraud pattern is a sticker with a different code pasted over the merchant's printed one. Raised edges, different paper or a crooked code on a neat stand deserve a second look. A code on the merchant's own screen or terminal is harder to tamper with than a laminated card at a busy counter.
- Check the amount on dynamic codes. Compare the embedded amount with your bill before confirming; you cannot edit it, which protects you from mis-keying but not from the wrong code.
- Never pay a code sent in a chat by someone claiming to be a merchant, a courier or a refund desk. Legitimate refunds do not require you to scan anything.
- Remember the direction of money. Scanning a QR Ph code can only lead to a payment you confirm. Anyone telling you to scan a code to receive money is describing a payment you are about to make.
For travelers: foreign wallet apps and card apps generally cannot pay a QR Ph code directly, because the payment must originate from a participant in the Philippine rail. In practice you need a Philippine wallet or bank account, or you ask whether the merchant accepts cards through a separate arrangement. Cross-border arrangements exist and are expanding, but assume nothing until the confirmation screen appears.
When a business needs a merchant code instead, and how it gets one
A merchant, or person-to-merchant, code is a different product from the personal code even though it looks and scans identically. It is issued by an acquiring participant, a bank, an e-money issuer or a payment operator, after merchant onboarding; the business cannot self-generate a valid one, because the account identifiers inside it belong to the acquirer's merchant account structure. Documents and steps are in how to become a QRPH merchant; here I only want the choice to be clear.
| Dimension | Personal (P2P) code | Static merchant code | Dynamic merchant code |
|---|---|---|---|
| Who creates it | The account holder, in the app | The acquiring participant, after onboarding | The acquirer's system or gateway API, per order |
| Name shown to the payer | The individual's registered name | The registered business or trade name | The registered business or trade name |
| Amount | Typed by the payer, or embedded for a one-off | Typed by the payer | Embedded and locked |
| Reference | None unless the payer types a note | None, or a fixed store identifier | Your order number, in additional data |
| Reconciliation | Manual, by name and time | Manual or semi-automatic, by amount and time | Automatic, by order number through a webhook |
| Settlement account | Personal account | Business settlement account | Business settlement account |
| Fits | Friends, occasional sales, splitting a bill | Low-volume counters with no system | E-commerce, apps, busy counters, anything with a system |
The static versus dynamic decision has its own page, static vs dynamic QR codes for Philippines merchants. The short version: the moment two transactions are in flight at once, static codes produce unmatched payments, and the fix is dynamic codes with the order number embedded.
Compliance note: the entity that issues your merchant code and settles your QR Ph collections must be registered with the Bangko Sentral ng Pilipinas as an Operator of Payment Systems or licensed as an Electronic Money Issuer. Check the BSP OPS/EMI standing of the name on your contract before you print a single code.
For developers: generating a dynamic QR Ph code per order
Integrating through a gateway, you never assemble the EMVCo string yourself. You create an order, request a dynamic QR Ph code for it, and receive the payload string, usually with a hosted image and an expiry. You render the payload at checkout or on the terminal, the customer scans it with any participating app, and the gateway calls your webhook when the payment clears. Where settlement lands for a foreign-owned business is covered in QRPH collection services for international businesses. The request, with the amount as a placeholder:
curl -X POST https://apix.example.com/api/qrph/create \
-H "Content-Type: application/json" \
-H "X-Merchant-Id: M12345" \
-d '{
"merchant_order_no": "SO-20261024-0001",
"payment_method": "QRPH",
"qr_type": "DYNAMIC",
"amount": "100.00",
"currency": "PHP",
"reference": "SO-20261024-0001",
"notify_url": "https://shop.example.com/payments/notify",
"sign": "<HMAC-SHA256 of sorted fields>"
}'
Three rules I enforce. One code per order, never reused: a dynamic code paid twice is a reconciliation incident. Expire the code with the order and generate a fresh one if the customer returns. Put your own order number in the request so it travels inside the code's additional data and comes back in the webhook. The handler then does four things in a fixed order: verify the signature, match order number and amount against your own record, transition state idempotently, fulfil.
const crypto = require('crypto');
const SECRET = process.env.GATEWAY_SECRET;
function sign(payload) {
const base = Object.keys(payload).sort()
.map(k => k + '=' + payload[k]).join('&');
return crypto.createHmac('sha256', SECRET).update(base).digest('hex');
}
app.post('/payments/notify', async (req, res) => {
const { sign: theirSign, ...data } = req.body;
const ours = sign(data);
if (ours.length !== theirSign.length ||
!crypto.timingSafeEqual(Buffer.from(ours), Buffer.from(theirSign))) {
return res.status(400).send('bad signature');
}
const order = await orders.find(data.merchant_order_no);
if (!order || order.amount !== data.amount || order.currency !== data.currency) {
return res.status(409).send('order mismatch');
}
if (data.status === 'SUCCESS' && order.state !== 'PAID') {
order.state = 'PAID'; // idempotent: a retried webhook changes nothing
order.rail = 'QRPH';
order.payer_participant = data.payer_participant; // which wallet or bank the customer paid from
order.rail_reference = data.instapay_reference; // what the acquirer asks for in a query
await orders.save(order);
await fulfil(order);
}
res.status(200).send('ok');
});
Notice what is stored: which participant the payer used, because the same code is paid from GCash one minute and from a bank app the next, and the rail reference, which is what the acquirer will ask for if a payment is ever queried. Treat a repeated webhook as normal; gateways retry, and the handler must return the same answer without fulfilling twice.
Frequently asked questions
Is a GCash QR code the same as a QR Ph code?
Since 1 July 2023, the payment code a GCash user or merchant presents is a QR Ph code displayed by GCash, and the same holds for Maya and participating banks. Proprietary wallet-only payment codes were required to be disabled from that date.
Can I pay a QR Ph code with a foreign card or a wallet app from home?
Generally not directly, because the payment must originate from a participant in the Philippine rail. You need a Philippine wallet or bank account, or the merchant needs a separate cross-border or card acceptance arrangement.
Can I print my personal QR Ph code and use it at my shop?
You can, and some small sellers do, but payers see your personal name, there is no order reference, and settlement goes to a personal account under personal terms. With steady volume, get a merchant code from an acquiring participant.
Does a QR Ph code expire?
A static code stays valid as long as the underlying account does. A dynamic code is tied to one payment and carries an expiry set by whoever generated it; after that the paying app refuses it.
The name shown after scanning differs from the shop's name. What now?
Do not confirm. Ask whose account it is. An owner's personal account or a holding company's name can be explained in a sentence; if the explanation is vague, pay another way.
Can I generate the QR Ph string myself with a QR library?
You can render the image from a payload string with any library, and that is often how a dynamic code reaches a checkout page. You cannot originate the account identifiers inside the string; those come from your acquiring participant or gateway, so you request the payload rather than build it.
Do I need a different code for each wallet my customers use?
No. One QR Ph code, personal or merchant, is paid from any participating wallet or bank app. That single fact is what the standard exists to deliver.
Where I land
A QR Ph code is a small structured string in a square: a participant, an account, a registered name, sometimes an amount and a reference, and a checksum. Individuals make their own in a few taps and should read the confirmation screen like a contract. Businesses get theirs from an acquiring participant and should move to dynamic codes the day two orders overlap. Developers never build the string; they request it per order and let the webhook, not the customer's screenshot, decide when an order is paid.
SMARTPAY holds BSP OPS/EMI licenses and provides merchants in the Philippines, including foreign-owned businesses, with QRPH, GCash, Maya, InstaPay and PESONet collection and payout integration: dynamic QR Ph codes generated per order, signed webhooks and settlement files that reconcile by order number.
If you are putting QR Ph codes on a counter or a checkout and want the merchant code, the API and the settlement account set up as one project, reach out on Telegram at @LouisSMARTPAY or email [email protected].