feat: 영수증 인쇄를 TCP→DLL 방식으로 전환

TCP 소켓(PRINTSTART/PRINTEND) 대신 AllThatPayCatReqMC(iCmd=2)로
DLL→MMF 경유 인쇄. GW 포트 변경에 영향받지 않음.

- ProcessPrint: TCP 소켓 제거, DLL CatReqMC(2) 호출로 대체
- FindGWPort: 8080/7779 포트 제외 로직 추가
- 실패 시 FindGWPort+ConnectDLL 재연결 후 1회 재시도
- build.bat, test_print_dll.ps1 추가 (DLL 인쇄 단독 테스트용)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
청춘약국
2026-04-11 10:47:37 +09:00
parent 7f66ee82b6
commit c658ac4c43
4 changed files with 205 additions and 1 deletions

View File

@@ -110,7 +110,9 @@ namespace PaymentBridge
var addr = parts[1];
if (addr.Contains(":"))
{
GW_PORT = addr.Split(':')[1];
string port = addr.Split(':')[1];
if (port == "8080" || port == "7779") continue; // 웹UI/Bridge 포트 제외
GW_PORT = port;
Log("GW Port found: " + GW_PORT);
break;
}
@@ -220,6 +222,12 @@ namespace PaymentBridge
var data = json.Deserialize<dynamic>(body);
responseString = ProcessCashReceipt("B2", data);
}
else if (path == "/api/print" && request.HttpMethod == "POST")
{
var body = ReadBody(request);
var data = json.Deserialize<dynamic>(body);
responseString = ProcessPrint(data);
}
else
{
responseString = json.Serialize(new { ok = false, error = "Unknown endpoint" });
@@ -419,6 +427,89 @@ namespace PaymentBridge
Console.WriteLine(message);
}
static string ProcessPrint(dynamic data)
{
// POS에서 받은 영수증 데이터를 DLL 경유로 인쇄 (MMF → AllthatpayClient → 프린터)
// data: { text: "영수증텍스트", qr_url: "https://...", qr_points: 100 }
try
{
string text = "";
string qrUrl = "";
int qrPoints = 0;
try { text = data["text"] ?? ""; } catch { }
try { qrUrl = data["qr_url"] ?? ""; } catch { }
try { qrPoints = Convert.ToInt32(data["qr_points"]); } catch { }
if (string.IsNullOrEmpty(text))
return json.Serialize(new { ok = false, error = "text is empty" });
// QR 적립 안내 텍스트 추가
if (!string.IsNullOrEmpty(qrUrl) && qrPoints > 0)
{
text += "\n------------------------------------------------\n";
text += " ★ 마일리지 적립 ★\n";
text += " QR 스캔하고 " + qrPoints.ToString("N0") + "P 받으세요!\n";
text += " (유효기간: 30일)\n";
text += "------------------------------------------------\n";
}
// EUC-KR 인코딩
Encoding eucKr = Encoding.GetEncoding("euc-kr");
byte[] textBytes = eucKr.GetBytes(text);
// 패킷 조립: text + FS×4 + ATQR_URL (DLL 방식 — PRINTSTART/END 불필요)
var packet = new System.Collections.Generic.List<byte>();
packet.AddRange(textBytes);
// QR 코드 추가 (ATQR_ 프로토콜)
if (!string.IsNullOrEmpty(qrUrl))
{
byte fs = 0x1C;
packet.Add(fs); packet.Add(fs); packet.Add(fs); packet.Add(fs);
packet.AddRange(Encoding.ASCII.GetBytes("ATQR_" + qrUrl));
}
byte[] buf = packet.ToArray();
// DLL 미연결 시 재연결
if (!isConnected)
{
FindGWPort();
ConnectDLL();
}
// DLL 경유 인쇄 (iCmd=2)
int result = AllThatPayCatReqMC(2, buf, buf.Length);
if (result != 0)
{
Log("Print OK (DLL): " + text.Length + " chars, QR=" + (!string.IsNullOrEmpty(qrUrl)));
return json.Serialize(new { ok = true, method = "dll", has_qr = !string.IsNullOrEmpty(qrUrl), bytes_sent = buf.Length });
}
// DLL 실패 → 재연결 후 1회 재시도
Log("Print DLL failed, reconnecting...");
FindGWPort();
ConnectDLL();
result = AllThatPayCatReqMC(2, buf, buf.Length);
if (result != 0)
{
Log("Print OK (DLL retry): " + text.Length + " chars, QR=" + (!string.IsNullOrEmpty(qrUrl)));
return json.Serialize(new { ok = true, method = "dll_retry", has_qr = !string.IsNullOrEmpty(qrUrl), bytes_sent = buf.Length });
}
return json.Serialize(new { ok = false, error = "DLL CatReqMC(2) returned 0" });
}
catch (Exception ex)
{
Log("ProcessPrint error: " + ex.Message);
return json.Serialize(new { ok = false, error = ex.Message });
}
}
static void Cleanup()
{
try