promptpay-qrcode 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,9 +65,17 @@ generateBillPayment({
65
65
  dynamic: true, // optional; force POI (true='12', false='11')
66
66
  merchantName: 'MY SHOP', // optional (tag 59)
67
67
  merchantCity: 'BANGKOK', // optional (tag 60)
68
+ additionalData: '07160000…', // optional raw tag 62 (e.g. terminal label sub-TLV)
69
+ countryCode: 'TH', // optional (tag 58, default 'TH')
68
70
  });
69
71
  ```
70
72
 
73
+ Tag order matches real Thai bill-payment QRs (`00,01,30,58,53,…,62,63` — note
74
+ `58` before `53`), so a decoded bill-payment QR round-trips **byte-for-byte**:
75
+ `generateBillPayment({ ...account, ...transaction })` from a `detach()` of an
76
+ SCB/Mae Manee QR reproduces the original exactly (including its tag-62 terminal
77
+ label).
78
+
71
79
  The Biller ID and references are issued/defined by your bank (for SCB, via the
72
80
  Mae Manee / Business QR onboarding). `ref1` is required; `ref2` is optional.
73
81
 
@@ -298,7 +306,7 @@ The second `options` argument is passed straight through to `qrcode`
298
306
  | Function | Returns |
299
307
  | --- | --- |
300
308
  | `generatePromptPay({ mobile \| nationalId \| ewallet, amount?, dynamic? })` | payload string |
301
- | `generateBillPayment({ billerId, ref1, ref2?, amount?, dynamic?, merchantName?, merchantCity? })` | payload string |
309
+ | `generateBillPayment({ billerId, ref1, ref2?, amount?, dynamic?, merchantName?, merchantCity?, additionalData?, countryCode? })` | payload string |
302
310
  | `generateKShopQR(amount, reference, config)` | payload string |
303
311
  | `KSHOP_DEFAULTS` / `REQUIRED_FIELDS` | KShop structural defaults / required field list |
304
312
  | `decode(payload)` | structured decode + CRC validation |
package/decode.js CHANGED
@@ -291,6 +291,10 @@ function detach(qr) {
291
291
  if (t30['01'] != null) account.billerId = t30['01'];
292
292
  if (f['59'] != null) account.merchantName = f['59'];
293
293
  if (f['60'] != null) account.merchantCity = f['60'];
294
+ if (f['58'] != null) account.countryCode = f['58'];
295
+ // Tag 62 (Additional Data) — keep the raw inner string so it round-trips.
296
+ const raw62 = flattenTag62(d.tags);
297
+ if (raw62 != null) account.additionalData = raw62;
294
298
  transaction = {
295
299
  ref1: t30['02'],
296
300
  ref2: t30['03'] != null ? t30['03'] : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptpay-qrcode",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Zero-dependency PromptPay QR payload generator (PromptPay P2P, Tag 30 bill payment / Mae Manee, and KShop)",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
package/promptpay.js CHANGED
@@ -115,9 +115,18 @@ function generatePromptPay({ mobile, nationalId, ewallet, amount, dynamic } = {}
115
115
  * @param {boolean} [opts.dynamic] Force POI: true='12', false='11'. Auto if omitted.
116
116
  * @param {string} [opts.merchantName] Merchant name (tag 59).
117
117
  * @param {string} [opts.merchantCity] Merchant city (tag 60).
118
+ * @param {string} [opts.additionalData] Raw Additional Data Field (tag 62) value,
119
+ * e.g. terminal label sub-TLV "0716...".
120
+ * @param {string} [opts.countryCode] Country code (tag 58). Default 'TH'.
118
121
  * @returns {string} The EMVCo QR payload including CRC.
122
+ *
123
+ * Tag order follows the layout used by real Thai bill-payment QRs (SCB / Mae
124
+ * Manee): `00,01,30,58,53,[54],[59],[60],[62],63` — note `58` precedes `53`.
125
+ * This lets a decoded bill-payment QR round-trip byte-for-byte.
119
126
  */
120
- function generateBillPayment({ billerId, ref1, ref2, amount, dynamic, merchantName, merchantCity } = {}) {
127
+ function generateBillPayment({
128
+ billerId, ref1, ref2, amount, dynamic, merchantName, merchantCity, additionalData, countryCode,
129
+ } = {}) {
121
130
  if (!billerId) throw new Error('billerId is required');
122
131
  if (!ref1) throw new Error('ref1 is required');
123
132
 
@@ -132,13 +141,14 @@ function generateBillPayment({ billerId, ref1, ref2, amount, dynamic, merchantNa
132
141
  payload += tlv('00', '01'); // payload format indicator
133
142
  payload += tlv('01', poiMethod(dynamic, hasAmount)); // dynamic vs static
134
143
  payload += tlv('30', merchantAccount); // bill payment merchant account info
144
+ payload += tlv('58', countryCode || COUNTRY_TH); // country (before currency, per Thai QR layout)
135
145
  payload += tlv('53', CURRENCY_THB);
136
146
  if (hasAmount) {
137
147
  payload += tlv('54', Number(amount).toFixed(2));
138
148
  }
139
- payload += tlv('58', COUNTRY_TH);
140
149
  if (merchantName) payload += tlv('59', merchantName);
141
150
  if (merchantCity) payload += tlv('60', merchantCity);
151
+ if (additionalData) payload += tlv('62', String(additionalData));
142
152
 
143
153
  payload += '6304' + crc16Hex(payload + '6304');
144
154
  return payload;