diff --git a/Assets/SamplePayment.cs b/Assets/SamplePayment.cs index 735c166..98b2967 100644 --- a/Assets/SamplePayment.cs +++ b/Assets/SamplePayment.cs @@ -27,7 +27,7 @@ namespace Darkmatter.Fonepay.Samples var fonepay = new FonepayClient(); var request = new QrRequest { - amount = 1, + amount = 0.5f, remarks1 = "mausham ko paisa" }; var qr = await fonepay.PurchaseAsync(request, destroyCancellationToken); @@ -45,9 +45,9 @@ namespace Darkmatter.Fonepay.Samples qr.thirdpartyQrWebSocketUrl, onQrVerified: v => Debug.Log($"Fonepay QR verified: {v}"), ct: destroyCancellationToken); - + Debug.Log($"{JsonUtility.ToJson(payment)}"); var ok = payment.Outcome == PaymentOutcome.Complete; - + qrImage.gameObject.SetActive(false); successObject.SetActive(ok); failedObject.SetActive(!ok); diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/API/FonepayApiClient.cs b/Packages/com.voidbotz.fonepayunity/Runtime/API/FonepayApiClient.cs index 7de4e55..3c31aa9 100644 --- a/Packages/com.voidbotz.fonepayunity/Runtime/API/FonepayApiClient.cs +++ b/Packages/com.voidbotz.fonepayunity/Runtime/API/FonepayApiClient.cs @@ -177,14 +177,29 @@ namespace Darkmatter.Fonepay return new QrResult { message = response.message, - qrCode = string.IsNullOrEmpty(response.qrMessage) - ? null - : FonepayQRGenerator.GenerateTexture(response.qrMessage), + qrCode = TryGenerateQr(response.qrMessage), status = response.status, statusCode = response.statusCode, success = response.success, thirdpartyQrWebSocketUrl = response.thirdpartyQrWebSocketUrl, + qrMessage = response.qrMessage, }; } + + private static Texture2D TryGenerateQr(string qrMessage) + { + if (string.IsNullOrEmpty(qrMessage)) + return null; + try + { + return FonepayQRGenerator.GenerateTexture(qrMessage); + } + catch (Exception ex) + { + Debug.LogWarning($"Fonepay QR render failed ({ex.Message}). " + + "Use QrResult.qrMessage with an external renderer."); + return null; + } + } } } \ No newline at end of file diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/API/FonepayWebsocketClient.cs b/Packages/com.voidbotz.fonepayunity/Runtime/API/FonepayWebsocketClient.cs index 1738d3d..0a19d30 100644 --- a/Packages/com.voidbotz.fonepayunity/Runtime/API/FonepayWebsocketClient.cs +++ b/Packages/com.voidbotz.fonepayunity/Runtime/API/FonepayWebsocketClient.cs @@ -13,6 +13,7 @@ namespace Darkmatter.Fonepay internal event Action OnQrVerified; internal event Action> OnPaymentReceived; internal event Action OnRawMessage; + internal event Action OnClosed; private ClientWebSocket _client; private CancellationTokenSource _cts; @@ -51,6 +52,7 @@ namespace Darkmatter.Fonepay CancellationToken cancellationToken) { var buffer = new byte[4096]; + Exception error = null; try { @@ -70,9 +72,9 @@ namespace Darkmatter.Fonepay { // Expected during disconnect. } - catch (WebSocketException) + catch (WebSocketException ex) { - // Network disconnect or broken socket. + error = ex; } catch (ObjectDisposedException) { @@ -80,7 +82,11 @@ namespace Darkmatter.Fonepay } catch (Exception ex) { - Console.WriteLine($"WebSocket receive error: {ex.Message}"); + error = ex; + } + finally + { + OnClosed?.Invoke(error); } } diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/Models/QrResponse.cs b/Packages/com.voidbotz.fonepayunity/Runtime/Models/QrResponse.cs index 61fff06..a655cf5 100644 --- a/Packages/com.voidbotz.fonepayunity/Runtime/Models/QrResponse.cs +++ b/Packages/com.voidbotz.fonepayunity/Runtime/Models/QrResponse.cs @@ -12,6 +12,7 @@ namespace Darkmatter.Fonepay public int statusCode; public bool success; public string thirdpartyQrWebSocketUrl; + public string qrMessage; } diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/Models/Websocket/QRPaymentStatus.cs b/Packages/com.voidbotz.fonepayunity/Runtime/Models/Websocket/QRPaymentStatus.cs index 1992e51..1771464 100644 --- a/Packages/com.voidbotz.fonepayunity/Runtime/Models/Websocket/QRPaymentStatus.cs +++ b/Packages/com.voidbotz.fonepayunity/Runtime/Models/Websocket/QRPaymentStatus.cs @@ -7,7 +7,7 @@ namespace Darkmatter.Fonepay { public string remarks1; public string remarks2; - public DateTime transactionDate; + public string transactionDate; public string productNumber; public float amount; public string message; diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/Models/Websocket/WebsocketMessage.cs b/Packages/com.voidbotz.fonepayunity/Runtime/Models/Websocket/WebsocketMessage.cs index a330efb..f733975 100644 --- a/Packages/com.voidbotz.fonepayunity/Runtime/Models/Websocket/WebsocketMessage.cs +++ b/Packages/com.voidbotz.fonepayunity/Runtime/Models/Websocket/WebsocketMessage.cs @@ -9,7 +9,6 @@ namespace Darkmatter.Fonepay public string merchantId; public string deviceId; public string transactionStatus; - private T _status; - public T Status => _status ??= JsonUtility.FromJson(transactionStatus); + public T Status => JsonUtility.FromJson(transactionStatus); } } \ No newline at end of file diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/Plugins.meta b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins.meta new file mode 100644 index 0000000..0b44ed1 --- /dev/null +++ b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: debdfefd8b01242e8b95f0e29bb09c0d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder-LICENSE.txt b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder-LICENSE.txt new file mode 100644 index 0000000..90b272f --- /dev/null +++ b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder-LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2025 Raffael Herrmann +Copyright (c) 2024-2025 Shane Krueger + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder-LICENSE.txt.meta b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder-LICENSE.txt.meta new file mode 100644 index 0000000..af80b69 --- /dev/null +++ b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder-LICENSE.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3902a28335b7043bf831aab193145f4c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder.dll b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder.dll new file mode 100644 index 0000000..2d2d7f2 Binary files /dev/null and b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder.dll differ diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder.dll.meta b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder.dll.meta new file mode 100644 index 0000000..c34b382 --- /dev/null +++ b/Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ecb337d209ad74a598de572b4e034307 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Capacity.cs b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Capacity.cs deleted file mode 100644 index 4a8ddb3..0000000 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Capacity.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; - -namespace Darkmatter.Fonepay -{ - public static partial class FonepayQRGenerator - { - // [version-1][eccLevel] = (totalCodewords, ecCodewordsPerBlock, blocks) - static readonly (int total, int ecPerBlock, int blocks)[,] _caps = - { - { (19, 7, 1), (16, 10, 1), (13, 13, 1), (9, 17, 1) }, - { (34, 10, 1), (28, 16, 1), (22, 22, 1), (16, 28, 1) }, - { (55, 15, 1), (44, 26, 1), (34, 18, 2), (26, 22, 2) }, - { (80, 20, 2), (64, 18, 2), (48, 26, 4), (36, 16, 4) }, - { (108, 26, 2), (86, 24, 2), (62, 18, 2), (46, 22, 2) }, - { (136, 18, 4), (108, 16, 4), (76, 24, 4), (60, 28, 4) }, - { (156, 20, 4), (124, 18, 4), (88, 18, 6), (66, 26, 4) }, - { (194, 24, 4), (154, 22, 4), (110, 22, 6), (86, 26, 4) }, - { (232, 30, 4), (182, 22, 5), (132, 20, 8), (100, 24, 4) }, - { (274, 18, 6), (216, 26, 6), (154, 24, 8), (122, 28, 6) }, - }; - - static int DataCodewords(int ver, EccLevel ecc) - { - var (total, ecPer, blks) = _caps[ver - 1, (int)ecc]; - return total - ecPer * blks; - } - - static (int version, (int total, int ecPerBlock, int blocks) ecBlocks) - ChooseVersion(int byteLen, EccLevel ecc) - { - for (int v = 1; v <= 10; v++) - { - int dc = DataCodewords(v, ecc); - if (dc >= byteLen + 2) - return (v, _caps[v - 1, (int)ecc]); - } - throw new Exception($"Data too large for versions 1-10 (byte mode, ecc={ecc})"); - } - } -} diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Capacity.cs.meta b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Capacity.cs.meta deleted file mode 100644 index a2e8c11..0000000 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Capacity.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: a611e576dae8b4ff6b15c314c1246cf1 \ No newline at end of file diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Codewords.cs b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Codewords.cs deleted file mode 100644 index 01d3588..0000000 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Codewords.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Darkmatter.Fonepay -{ - public static partial class FonepayQRGenerator - { - static byte[] BuildCodewords(byte[] data, int version, EccLevel ecc, - (int total, int ecPerBlock, int blocks) ecBlocks) - { - var bits = new List(); - bits.AddRange(new[] { false, true, false, false }); // byte mode - int len = data.Length; - for (int i = 7; i >= 0; i--) bits.Add(((len >> i) & 1) == 1); - foreach (byte b in data) - for (int i = 7; i >= 0; i--) - bits.Add(((b >> i) & 1) == 1); - - int dcBytes = DataCodewords(version, ecc); - int targetBits = dcBytes * 8; - for (int i = 0; i < 4 && bits.Count < targetBits; i++) bits.Add(false); - while (bits.Count % 8 != 0) bits.Add(false); - - bool[] padA = { true, true, true, false, true, true, false, false }; - bool[] padB = { false, false, false, true, false, false, false, true }; - int pi = 0; - while (bits.Count < targetBits) - { - bits.AddRange(pi % 2 == 0 ? padA : padB); - pi++; - } - - byte[] dc = new byte[dcBytes]; - for (int i = 0; i < dcBytes; i++) - for (int b = 0; b < 8; b++) - if (bits[i * 8 + b]) - dc[i] |= (byte)(1 << (7 - b)); - - int blocks = ecBlocks.blocks; - int ecPer = ecBlocks.ecPerBlock; - int blockSize = dcBytes / blocks; - byte[][] dcBlocks = new byte[blocks][]; - byte[][] ecBlocksArr = new byte[blocks][]; - for (int i = 0; i < blocks; i++) - { - dcBlocks[i] = new byte[blockSize]; - Array.Copy(dc, i * blockSize, dcBlocks[i], 0, blockSize); - ecBlocksArr[i] = ReedSolomon(dcBlocks[i], ecPer); - } - - var result = new List(); - for (int i = 0; i < blockSize; i++) - for (int b = 0; b < blocks; b++) - result.Add(dcBlocks[b][i]); - for (int i = 0; i < ecPer; i++) - for (int b = 0; b < blocks; b++) - result.Add(ecBlocksArr[b][i]); - - return result.ToArray(); - } - } -} diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Codewords.cs.meta b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Codewords.cs.meta deleted file mode 100644 index 97e56eb..0000000 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Codewords.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: e67574ccbe68944928d706bd99d785ca \ No newline at end of file diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Matrix.cs b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Matrix.cs deleted file mode 100644 index fba66bd..0000000 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Matrix.cs +++ /dev/null @@ -1,234 +0,0 @@ -using System; - -namespace Darkmatter.Fonepay -{ - public static partial class FonepayQRGenerator - { - static bool[,] BuildMatrix(int version, byte[] codewords) - { - int size = version * 4 + 17; - var matrix = new bool[size, size]; - var reserved = new bool[size, size]; - - PlaceFinder(matrix, reserved, 0, 0); - PlaceFinder(matrix, reserved, 0, size - 7); - PlaceFinder(matrix, reserved, size - 7, 0); - PlaceTiming(matrix, reserved, size); - PlaceDarkModule(matrix, reserved, version); - if (version >= 2) PlaceAlignment(matrix, reserved, version); - ReserveFormat(reserved, size); - PlaceData(matrix, reserved, codewords, size); - - int bestPenalty = int.MaxValue; - bool[,] bestMatrix = null; - for (int m = 0; m < 8; m++) - { - var candidate = (bool[,])matrix.Clone(); - ApplyMask(candidate, reserved, m, size); - ApplyFormatInfo(candidate, reserved, 1, m, size); - int penalty = CalcPenalty(candidate, size); - if (penalty < bestPenalty) - { - bestPenalty = penalty; - bestMatrix = (bool[,])candidate.Clone(); - } - } - return bestMatrix; - } - - static void PlaceFinder(bool[,] m, bool[,] r, int row, int col) - { - for (int dr = -1; dr <= 7; dr++) - for (int dc = -1; dc <= 7; dc++) - { - int rr = row + dr, cc = col + dc; - if (rr < 0 || cc < 0 || rr >= m.GetLength(0) || cc >= m.GetLength(1)) continue; - r[rr, cc] = true; - bool inOuter = dr >= 0 && dr <= 6 && dc >= 0 && dc <= 6; - bool inBorder = (dr == 0 || dr == 6 || dc == 0 || dc == 6) && inOuter; - bool inInner = dr >= 2 && dr <= 4 && dc >= 2 && dc <= 4; - m[rr, cc] = !(dr == -1 || dc == -1 || dr == 7 || dc == 7) && (inBorder || inInner); - } - } - - static void PlaceTiming(bool[,] m, bool[,] r, int size) - { - for (int i = 8; i < size - 8; i++) - { - bool v = i % 2 == 0; - m[6, i] = v; r[6, i] = true; - m[i, 6] = v; r[i, 6] = true; - } - } - - static void PlaceDarkModule(bool[,] m, bool[,] r, int ver) - { - int row = ver * 4 + 9; - m[row, 8] = true; - r[row, 8] = true; - } - - static readonly int[][] _alignCenters = - { - new int[] { }, - new[] { 6, 18 }, - new[] { 6, 22 }, - new[] { 6, 26 }, - new[] { 6, 30 }, - new[] { 6, 34 }, - new[] { 6, 22, 38 }, - new[] { 6, 24, 42 }, - new[] { 6, 26, 46 }, - new[] { 6, 28, 50 }, - }; - - static void PlaceAlignment(bool[,] m, bool[,] r, int version) - { - int[] centers = _alignCenters[version - 1]; - foreach (int row in centers) - foreach (int col in centers) - { - if (r[row, col]) continue; - for (int dr = -2; dr <= 2; dr++) - for (int dc = -2; dc <= 2; dc++) - { - bool dark = Math.Abs(dr) == 2 || Math.Abs(dc) == 2 || (dr == 0 && dc == 0); - m[row + dr, col + dc] = dark; - r[row + dr, col + dc] = true; - } - } - } - - static void ReserveFormat(bool[,] r, int size) - { - for (int i = 0; i <= 8; i++) - { - r[8, i] = true; - r[i, 8] = true; - } - for (int i = size - 8; i < size; i++) - { - r[8, i] = true; - r[i, 8] = true; - } - } - - static void PlaceData(bool[,] m, bool[,] r, byte[] cw, int size) - { - int cwIdx = 0, bitIdx = 7; - bool upward = true; - int col = size - 1; - - while (col > 0) - { - if (col == 6) col--; - for (int rowStep = 0; rowStep < size; rowStep++) - { - int row = upward ? size - 1 - rowStep : rowStep; - for (int c = 0; c < 2; c++) - { - int cc = col - c; - if (r[row, cc]) continue; - bool bit = cwIdx < cw.Length && ((cw[cwIdx] >> bitIdx) & 1) == 1; - m[row, cc] = bit; - if (--bitIdx < 0) - { - bitIdx = 7; - cwIdx++; - } - } - } - upward = !upward; - col -= 2; - } - } - - static void ApplyMask(bool[,] m, bool[,] r, int mask, int size) - { - for (int row = 0; row < size; row++) - for (int col = 0; col < size; col++) - { - if (r[row, col]) continue; - bool flip = mask switch - { - 0 => (row + col) % 2 == 0, - 1 => row % 2 == 0, - 2 => col % 3 == 0, - 3 => (row + col) % 3 == 0, - 4 => (row / 2 + col / 3) % 2 == 0, - 5 => row * col % 2 + row * col % 3 == 0, - 6 => (row * col % 2 + row * col % 3) % 2 == 0, - 7 => ((row + col) % 2 + row * col % 3) % 2 == 0, - _ => false - }; - if (flip) m[row, col] ^= true; - } - } - - static readonly ushort[] _formatL = { 0x77C4, 0x72F3, 0x7DAA, 0x789D, 0x662F, 0x6318, 0x6C41, 0x6976 }; - static readonly ushort[] _formatM = { 0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0 }; - static readonly ushort[] _formatQ = { 0x355F, 0x3068, 0x3F31, 0x3A06, 0x24B4, 0x2183, 0x2EDA, 0x2BED }; - static readonly ushort[] _formatH = { 0x1689, 0x13BE, 0x1CE7, 0x19D0, 0x0762, 0x0255, 0x0D0C, 0x083B }; - - static void ApplyFormatInfo(bool[,] m, bool[,] r, int eccIndex, int mask, int size) - { - ushort[] table = eccIndex switch { 0 => _formatM, 1 => _formatL, 2 => _formatH, _ => _formatQ }; - ushort fmt = table[mask]; - - int[] seq = { 0, 1, 2, 3, 4, 5, 7, 8 }; - for (int i = 0; i < 8; i++) - { - bool bit = ((fmt >> (14 - i)) & 1) == 1; - m[8, seq[i]] = bit; - m[seq[i < 6 ? i : i + 1 < 8 ? i : i], 8] = bit; - } - - for (int i = 0; i < 7; i++) - { - bool bit = ((fmt >> i) & 1) == 1; - m[size - 1 - i, 8] = bit; - } - for (int i = 7; i < 15; i++) - { - bool bit = ((fmt >> i) & 1) == 1; - m[8, size - 15 + i] = bit; - } - } - - static int CalcPenalty(bool[,] m, int size) - { - int p = 0; - for (int r = 0; r < size; r++) - { - int run = 1; - for (int c = 1; c < size; c++) - { - if (m[r, c] == m[r, c - 1]) - { - run++; - if (run == 5) p += 3; - else if (run > 5) p++; - } - else run = 1; - } - run = 1; - for (int c = 1; c < size; c++) - { - if (m[c, r] == m[c - 1, r]) - { - run++; - if (run == 5) p += 3; - else if (run > 5) p++; - } - else run = 1; - } - } - - for (int r = 0; r < size - 1; r++) - for (int c = 0; c < size - 1; c++) - if (m[r, c] == m[r, c + 1] && m[r, c] == m[r + 1, c] && m[r, c] == m[r + 1, c + 1]) - p += 3; - return p; - } - } -} diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Matrix.cs.meta b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Matrix.cs.meta deleted file mode 100644 index 5976f5c..0000000 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.Matrix.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 6510d029572264a5b8aea6d31e302686 \ No newline at end of file diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.ReedSolomon.cs b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.ReedSolomon.cs deleted file mode 100644 index 7c1b938..0000000 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.ReedSolomon.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; - -namespace Darkmatter.Fonepay -{ - public static partial class FonepayQRGenerator - { - static readonly byte[] _exp = new byte[512]; - static readonly byte[] _log = new byte[256]; - - static FonepayQRGenerator() - { - int x = 1; - for (int i = 0; i < 255; i++) - { - _exp[i] = (byte)x; - _log[x] = (byte)i; - x <<= 1; - if (x >= 256) x ^= 0x11d; - } - for (int i = 255; i < 512; i++) _exp[i] = _exp[i - 255]; - } - - static byte GfMul(byte a, byte b) - { - if (a == 0 || b == 0) return 0; - return _exp[(_log[a] + _log[b]) % 255]; - } - - static byte GfPow(int b, int e) => _exp[(_log[(byte)b] * e) % 255]; - - static byte[] ReedSolomon(byte[] data, int ecCount) - { - byte[] gen = RsGenerator(ecCount); - byte[] msg = new byte[data.Length + ecCount]; - Array.Copy(data, msg, data.Length); - - for (int i = 0; i < data.Length; i++) - { - byte coef = msg[i]; - if (coef == 0) continue; - for (int j = 1; j < gen.Length; j++) - msg[i + j] ^= GfMul(gen[j], coef); - } - - byte[] ec = new byte[ecCount]; - Array.Copy(msg, data.Length, ec, 0, ecCount); - return ec; - } - - static byte[] RsGenerator(int degree) - { - byte[] g = { 1 }; - for (int i = 0; i < degree; i++) - { - byte[] ng = new byte[g.Length + 1]; - byte root = GfPow(2, i); - for (int j = 0; j < g.Length; j++) - { - ng[j] ^= GfMul(g[j], root); - ng[j + 1] ^= g[j]; - } - g = ng; - } - return g; - } - } -} diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.ReedSolomon.cs.meta b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.ReedSolomon.cs.meta deleted file mode 100644 index 083228a..0000000 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.ReedSolomon.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 049bdafac777b4e58bafafb2224e88a8 \ No newline at end of file diff --git a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.cs b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.cs index 4fd6d54..c64d4b5 100644 --- a/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.cs +++ b/Packages/com.voidbotz.fonepayunity/Runtime/QR/FonepayQRGenerator.cs @@ -1,11 +1,13 @@ +using System.Collections; +using QRCoder; using UnityEngine; namespace Darkmatter.Fonepay { /// - /// Pure C# QR Code generator. Byte mode, ECC L/M/Q/H, versions 1–10. + /// QR code generator backed by QRCoder (MIT). See Plugins/QRCoder-LICENSE.txt. /// - public static partial class FonepayQRGenerator + public static class FonepayQRGenerator { public enum EccLevel { L, M, Q, H } @@ -25,16 +27,21 @@ namespace Darkmatter.Fonepay Color dark = darkColor ?? Color.black; Color light = lightColor ?? Color.white; + var pixels = new Color[texSize * texSize]; for (int row = 0; row < size; row++) for (int col = 0; col < size; col++) { Color c = matrix[row, col] ? dark : light; + int yBase = (size - 1 - row) * pixelSize; + int xBase = col * pixelSize; for (int py = 0; py < pixelSize; py++) - for (int px = 0; px < pixelSize; px++) - tex.SetPixel(col * pixelSize + px, - (size - 1 - row) * pixelSize + py, c); + { + int rowStart = (yBase + py) * texSize + xBase; + for (int px = 0; px < pixelSize; px++) + pixels[rowStart + px] = c; + } } - + tex.SetPixels(pixels); tex.Apply(); return tex; } @@ -57,10 +64,26 @@ namespace Darkmatter.Fonepay { if (string.IsNullOrEmpty(text)) throw new System.ArgumentException("QR text must be non-empty", nameof(text)); - byte[] data = System.Text.Encoding.UTF8.GetBytes(text); - var (version, ecBlocks) = ChooseVersion(data.Length, ecc); - byte[] codewords = BuildCodewords(data, version, ecc, ecBlocks); - return BuildMatrix(version, codewords); + + using var data = new QRCodeGenerator().CreateQrCode(text, MapEcc(ecc), forceUtf8: true); + int size = data.ModuleMatrix.Count; + var matrix = new bool[size, size]; + for (int row = 0; row < size; row++) + { + BitArray bits = data.ModuleMatrix[row]; + for (int col = 0; col < size; col++) + matrix[row, col] = bits[col]; + } + return matrix; } + + static QRCodeGenerator.ECCLevel MapEcc(EccLevel e) => e switch + { + EccLevel.L => QRCodeGenerator.ECCLevel.L, + EccLevel.M => QRCodeGenerator.ECCLevel.M, + EccLevel.Q => QRCodeGenerator.ECCLevel.Q, + EccLevel.H => QRCodeGenerator.ECCLevel.H, + _ => QRCodeGenerator.ECCLevel.M + }; } }