QR and payment fixes
This commit is contained in:
@@ -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,7 +45,7 @@ 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ namespace Darkmatter.Fonepay
|
||||
internal event Action<bool> OnQrVerified;
|
||||
internal event Action<WebsocketMessage<QRPaymentStatus>> OnPaymentReceived;
|
||||
internal event Action<string> OnRawMessage;
|
||||
internal event Action<Exception> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Darkmatter.Fonepay
|
||||
public int statusCode;
|
||||
public bool success;
|
||||
public string thirdpartyQrWebSocketUrl;
|
||||
public string qrMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<T>(transactionStatus);
|
||||
public T Status => JsonUtility.FromJson<T>(transactionStatus);
|
||||
}
|
||||
}
|
||||
8
Packages/com.voidbotz.fonepayunity/Runtime/Plugins.meta
Normal file
8
Packages/com.voidbotz.fonepayunity/Runtime/Plugins.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: debdfefd8b01242e8b95f0e29bb09c0d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3902a28335b7043bf831aab193145f4c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder.dll
Normal file
BIN
Packages/com.voidbotz.fonepayunity/Runtime/Plugins/QRCoder.dll
Normal file
Binary file not shown.
@@ -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:
|
||||
@@ -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})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a611e576dae8b4ff6b15c314c1246cf1
|
||||
@@ -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<bool>();
|
||||
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<byte>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e67574ccbe68944928d706bd99d785ca
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6510d029572264a5b8aea6d31e302686
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 049bdafac777b4e58bafafb2224e88a8
|
||||
@@ -1,11 +1,13 @@
|
||||
using System.Collections;
|
||||
using QRCoder;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Darkmatter.Fonepay
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user