| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- using HttpServices;
- using Newtonsoft.Json.Linq;
- using System;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using UppPay.Forms;
- using Ini;
- using DGPosPay;
- namespace UppPay
- {
- public class UppPay
- {
- Thread thread;
- string errorCode = "-1";
- string errorMessage = "";
- JObject SMFormOutDataobj = new JObject();
- JObject result;
- JObject inParamsobj = new JObject();
- IniFile ini = new IniFile();
- Loading loading = new Loading();
- private static int _busy = 0; // 防重入闸门:支付进行中禁止再次调用
- //public string UPPPayControl(string inParams)
- //{
- // System.Diagnostics.Debugger.Launch();
- // string readData = "";
- // result = new JObject();
- // JObject jObject = new JObject();
- // try
- // {
- // inParamsobj = JObject.Parse(inParams);
- // string recordIDinfo = inParamsobj["recordIDinfo"].ToString();
- // var payInfo = inParamsobj["payInfo"];
- // var payListArr = inParamsobj["payList"];
- // string inPaywCode = "";
- // string inAuthcode = "";
- // if (payInfo != null)
- // {
- // inPaywCode = payInfo["paywCode"]?.ToString();
- // inAuthcode = payInfo["authcode"]?.ToString();
- // }
- // foreach (JObject obj in payListArr)
- // {
- // // 提取对象的属性值
- // string payWType = obj["payWType"]?.ToString();
- // string payWayCode = obj["payWayCode"]?.ToString();
- // string payAmt = obj["payAmt"]?.ToString();
- // string revTranFlag = obj["revTranFlag"]?.ToString();
- // if (payWType == "DGDLL")
- // {
- // //东莞光明
- // thread = new Thread(() => formPOSFormd(obj));
- // thread.SetApartmentState(ApartmentState.STA); //重点
- // thread.Start();
- // thread.Join();
- // if (errorCode != "0")
- // {
- // //错误退出
- // break;
- // }
- // }
- // else if (payWType == "ONLINE")
- // {
- // //线上扫码支付
- // string authcode = "";
- // if ((inPaywCode != "") && (inPaywCode == payWayCode))
- // {
- // ///前端界面传入了二维码
- // authcode = inAuthcode;
- // }
- // else
- // {
- // SMForm formSM = new SMForm();
- // JObject smreturnobj = formSMFormd(formSM);
- // errorCode = smreturnobj["errorCode"].ToString();
- // errorMessage = smreturnobj["errorMessage"].ToString();
- // if (errorCode != "0")
- // {
- // break;
- // }
- // authcode = smreturnobj["authcode"].ToString();
- // }
- // if (string.IsNullOrWhiteSpace(authcode))
- // {
- // errorCode = "-1";
- // errorMessage = "authcode 为空";
- // break;
- // }
- // obj.Add("authcode", authcode);
- // LoadingFormd();
- // thread = new Thread(() => payONLINE(obj));
- // thread.SetApartmentState(ApartmentState.STA); //重点
- // thread.Start();
- // thread.Join();
- // if (errorCode != "0")
- // {
- // //错误退出
- // break;
- // }
- // }
- // else
- // {
- // break;
- // errorCode = "-1";
- // errorMessage = "非第三支付方式";
- // }
- // }
- // }
- // catch (Exception ex2)
- // {
- // errorCode = "-1";
- // errorMessage = ex2.Message;
- // }
- // jObject.Add("errorCode", errorCode);
- // jObject.Add("errorMessage", errorMessage);
- // jObject.Add("result", result);
- // return jObject.ToString();
- //}
- #region 20260828 解决UI线城调用问题
- public string UPPPayControl(string inParams)
- {
- // 防重入:泵消息等待期间用户可能再次点支付,直接挡掉
- if (Interlocked.Exchange(ref _busy, 1) == 1)
- {
- JObject busyobj = new JObject();
- busyobj.Add("errorCode", "-1");
- busyobj.Add("errorMessage", "上一笔支付还在处理中,请稍候");
- busyobj.Add("result", new JObject());
- return busyobj.ToString();
- }
- JObject jObject = new JObject();
- try
- {
- result = new JObject();
- inParamsobj = JObject.Parse(inParams);
- string recordIDinfo = inParamsobj["recordIDinfo"].ToString();
- var payInfo = inParamsobj["payInfo"];
- var payListArr = inParamsobj["payList"];
- string inPaywCode = "";
- string inAuthcode = "";
- if (payInfo != null)
- {
- inPaywCode = payInfo["paywCode"]?.ToString();
- inAuthcode = payInfo["authcode"]?.ToString();
- }
- foreach (JObject obj in payListArr)
- {
- string payWType = obj["payWType"]?.ToString();
- string payWayCode = obj["payWayCode"]?.ToString();
- string payAmt = obj["payAmt"]?.ToString();
- string revTranFlag = obj["revTranFlag"]?.ToString();
- if (payWType == "DGDLL")
- {
- //东莞光明
- thread = new Thread(() => formPOSFormd(obj));
- thread.SetApartmentState(ApartmentState.STA); //重点
- thread.Start();
- WaitWithPump(thread); // 原来 thread.Join() 改为泵消息等待
- if (errorCode != "0")
- {
- //错误退出
- break;
- }
- }
- else if (payWType == "ONLINE")
- {
- //线上扫码支付
- string authcode = "";
- if ((inPaywCode != "") && (inPaywCode == payWayCode))
- {
- ///前端界面传入了二维码
- authcode = inAuthcode;
- }
- else
- {
- SMForm formSM = new SMForm();
- JObject smreturnobj = formSMFormd(formSM);
- errorCode = smreturnobj["errorCode"].ToString();
- errorMessage = smreturnobj["errorMessage"].ToString();
- if (errorCode != "0")
- {
- break;
- }
- authcode = smreturnobj["authcode"].ToString();
- }
- if (string.IsNullOrWhiteSpace(authcode))
- {
- errorCode = "-1";
- errorMessage = "authcode 为空";
- break;
- }
- obj.Add("authcode", authcode);
- // 显示 Loading(非模态,配合下方泵消息等待保持窗体响应)
- loading = new Loading();
- loading.Show();
- // 支付逻辑放后台线程,主线程泵消息等待
- thread = new Thread(() => payONLINE(obj));
- thread.IsBackground = true;
- thread.Start();
- WaitWithPump(thread); // 原来 thread.Join() 改为泵消息等待
- // 兜底关闭 Loading(worker 正常结束时已通过 SafeUI 关闭)
- SafeUI(() => loading.CloseLoading());
- if (errorCode != "0")
- {
- //错误退出
- break;
- }
- }
- else
- {
- errorCode = "-1";
- errorMessage = "非第三支付方式";
- break; // 注:原代码 break 在赋值前面,永远执行不到,这里顺手修正
- }
- }
- }
- catch (Exception ex2)
- {
- errorCode = "-1";
- errorMessage = ex2.Message;
- }
- finally
- {
- Interlocked.Exchange(ref _busy, 0);
- }
- jObject.Add("errorCode", errorCode);
- jObject.Add("errorMessage", errorMessage);
- jObject.Add("result", result);
- return jObject.ToString();
- }
- /// <summary>
- /// 带消息泵的等待:等待期间持续处理 Windows 消息,
- /// 保证 Loading 等窗体不出现“未响应”。
- /// </summary>
- private void WaitWithPump(Thread t)
- {
- while (t.IsAlive)
- {
- Application.DoEvents();
- Thread.Sleep(30);
- }
- Application.DoEvents(); // 新增:把 worker 排队的关闭消息处理掉
- }
- /// <summary>
- /// 跨线程安全更新 Loading 窗体(在 worker 线程中调用)
- /// </summary>
- private void SafeUI(MethodInvoker act)
- {
- try
- {
- if (loading != null && !loading.IsDisposed && loading.IsHandleCreated)
- {
- loading.BeginInvoke(act);
- }
- }
- catch (Exception)
- {
- // 窗体已被关闭/释放时忽略
- }
- }
- #endregion
- // 1. 方法签名必须改为 async Task<string>
- // 注意:调用这个方法的地方也需要相应调整(如果是按钮点击事件可以用 async void)
- //public async Task<string> UPPPayControl(string inParams)
- //{
- // // System.Diagnostics.Debugger.Launch(); // 调试时开启,发布时建议注释掉
- // string readData = "";
- // result = new JObject();
- // JObject jObject = new JObject();
- // try
- // {
- // inParamsobj = JObject.Parse(inParams);
- // string recordIDinfo = inParamsobj["recordIDinfo"].ToString();
- // var payInfo = inParamsobj["payInfo"];
- // var payListArr = inParamsobj["payList"];
- // string inPaywCode = "";
- // string inAuthcode = "";
- // if (payInfo != null)
- // {
- // inPaywCode = payInfo["paywCode"]?.ToString();
- // inAuthcode = payInfo["authcode"]?.ToString();
- // }
- // foreach (JObject obj in payListArr)
- // {
- // // 提取对象的属性值
- // string payWType = obj["payWType"]?.ToString();
- // string payWayCode = obj["payWayCode"]?.ToString();
- // string payAmt = obj["payAmt"]?.ToString();
- // string revTranFlag = obj["revTranFlag"]?.ToString();
- // if (payWType == "DGDLL")
- // {
- // // --- 东莞光明逻辑 ---
- // // 【修改点】使用 Task.Run 在后台线程执行,await 等待完成
- // // 这样主线程会去处理 Loading 动画,不会卡死
- // await Task.Run(() => formPOSFormd(obj));
- // if (errorCode != "0")
- // {
- // // 错误退出
- // break;
- // }
- // }
- // else if (payWType == "ONLINE")
- // {
- // // --- 线上扫码支付逻辑 ---
- // string authcode = "";
- // if ((inPaywCode != "") && (inPaywCode == payWayCode))
- // {
- // /// 前端界面传入了二维码
- // authcode = inAuthcode;
- // }
- // else
- // {
- // // 获取扫码结果
- // SMForm formSM = new SMForm();
- // // 注意:如果 formSMFormd 内部涉及 UI 操作,它必须在主线程运行
- // // 如果它是纯逻辑计算,可以包在 Task.Run 里;如果是弹窗,直接调用即可
- // JObject smreturnobj = formSMFormd(formSM);
- // errorCode = smreturnobj["errorCode"].ToString();
- // errorMessage = smreturnobj["errorMessage"].ToString();
- // if (errorCode != "0")
- // {
- // break;
- // }
- // authcode = smreturnobj["authcode"].ToString();
- // }
- // if (string.IsNullOrWhiteSpace(authcode))
- // {
- // errorCode = "-1";
- // errorMessage = "authcode 为空";
- // break;
- // }
- // obj.Add("authcode", authcode);
- // // 显示 Loading
- // LoadingFormd();
- // // 【修改点】核心修复处
- // // 原来:thread.Start(); thread.Join(); (这会卡死 UI)
- // // 现在:Task.Run 在后台跑支付逻辑,await 等待它结束
- // await Task.Run(() => payONLINE(obj));
- // if (errorCode != "0")
- // {
- // // 错误退出
- // break;
- // }
- // }
- // else
- // {
- // break; // 这里的 break 会导致下面的 errorCode 赋值无法执行,保留原样
- // errorCode = "-1";
- // errorMessage = "非第三支付方式";
- // }
- // }
- // }
- // catch (Exception ex2)
- // {
- // errorCode = "-1";
- // errorMessage = ex2.Message;
- // }
- // jObject.Add("errorCode", errorCode);
- // jObject.Add("errorMessage", errorMessage);
- // jObject.Add("result", result);
- // return jObject.ToString();
- //}
- /// <summary>
- /// 安全取值:字段缺失或为 null 时返回空串,而不是抛 NullReferenceException。
- /// 20261020 新增 —— 原代码大量使用 json["k"].ToString(),当 HIS 返回
- /// IRIS 原生错误结构({"errors":[...],"summary":...})时必然空引用,
- /// 真实报错会被外层 catch 吞成"对象引用未设置",排查时完全看不到真相。
- /// </summary>
- private string GetStr(JObject o, string key)
- {
- if (o == null) return "";
- JToken t;
- try { t = o[key]; } catch (Exception) { return ""; }
- if (t == null || t.Type == JTokenType.Null || t.Type == JTokenType.Undefined)
- {
- return "";
- }
- return t.ToString();
- }
- /// <summary>把关键诊断信息写入 UPPPay 日志目录。</summary>
- private void WriteDiag(string tag, string text)
- {
- try
- {
- IniFile tini = new IniFile();
- string p = tini.IniReadValue("LOG", "path");
- if (!string.IsNullOrWhiteSpace(p))
- {
- FileInfoClass.FileClass fc = new FileInfoClass.FileClass();
- fc.WriteLogFile(p, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " [DIAG][" + tag + "] " + text);
- }
- }
- catch (Exception) { }
- }
- private JObject payONLINE2()
- {
- SMFormOutDataobj = new JObject();
- loading.lblStatusText("请稍等........");
- try
- {
- for (int i = 0; i < 50; i++)
- {
- Thread.Sleep(500);
- loading.lblStatusText("" + i);
- }
- loading.CloseLoading();
- }
- catch (Exception ex2)
- {
- errorCode = "-1";
- errorMessage = "ReportFormd:" + ex2.Message;
- }
- errorCode = "-1";
- errorMessage = "ReportFormd:";
- return SMFormOutDataobj;
- }
- private void payONLINE(JObject InjObject)
- {
- //loading.lblStatusText("请稍等........");
- SafeUI(() => loading.lblStatusText("请稍等........"));
- try
- {
- string PayCode = ini.IniReadValue("SMPay", "PayCode");
- // 创建一个 JObject 对象
- JObject hisParamsobj = new JObject();
- // 创建一个 JArray 对象
- JArray paramsArray = new JArray();
- paramsArray.Add(InjObject);
- hisParamsobj["params"] = paramsArray;
- hisParamsobj["code"] = PayCode;
- hisParamsobj["session"] = inParamsobj["session"];
- //调用his进行数据Pos数据组织
- RestClient restClient = new RestClient();
- // 20261020:发起支付需等待病人输密码,必须用长超时档(默认90s,ini 可配)
- Task<string> task = restClient.HISHTTPServices(hisParamsobj.ToString(), "", "", "", RestClient.TimeoutPay); //调用HIS回调
- string hisResult = task.Result;
- JObject hisResultObject = JObject.Parse(hisResult);
- // 20261020:原代码直接 .ToString(),当 HIS 返回 IRIS 原生错误结构(无 errorCode 字段)
- // 会抛 NullReferenceException 并被外层吞成"对象引用未设置",真实错误信息全部丢失。
- // 这里改为带标记的安全读取。
- string code = GetStr(hisResultObject, "errorCode");
- string msg = GetStr(hisResultObject, "errorMessage");
- string callbackDataRaw = GetStr(hisResultObject, "callbackData");
- errorCode = code;
- errorMessage = msg;
- string callbackDatainfo = callbackDataRaw;
- if (errorCode == "")
- {
- // 没有 errorCode —— 原样把 HIS 的返回内容作为错误信息透出去
- errorCode = "-1";
- errorMessage = "HIS返回非预期结构: " + hisResult;
- WriteDiag("payONLINE", "HIS 响应缺少 errorCode 字段,原始返回: " + hisResult);
- }
- if (errorCode == "0")
- {
- ///支付成功
- result = JObject.Parse(GetStr(hisResultObject, "result"));
- }
- else if (errorCode == "USERPAYING")
- {
- if (callbackDatainfo == "")
- {
- errorCode = "-1";
- errorMessage = "USERPAYING 但未返回 callbackData,无法轮询";
- WriteDiag("payONLINE", "USERPAYING 缺少 callbackData,原始返回: " + hisResult);
- }
- else
- {
- //loading.lblStatusText("请告知病人手机输入支付密码........");
- SafeUI(() => loading.lblStatusText("请告知病人手机输入支付密码........"));
- //Thread.Sleep(3000);
- int countDownTime = int.Parse(ini.IniReadValue("Config", "waitTime"));
- int warningTime = int.Parse(ini.IniReadValue("Config", "warningTime"));
- for (int i = 0; i < countDownTime; i++)
- {
- //Thread.Sleep(1000);
- //loading.label2Text(countDownTime - i, warningTime);
- SafeUI(() => loading.label2Text(countDownTime - i, warningTime));
- JObject callbackDataobj = JObject.Parse(callbackDatainfo);
- // 查询/回写走短超时档,避免单次轮询吃掉整个时间预算
- Task<string> savetask = restClient.HISHTTPServices(callbackDataobj.ToString(), "", "", "", RestClient.TimeoutQuery); //调用HIS回调保存数据
- string hisSaveResult = savetask.Result;
- JObject hisSaveResultobj = JObject.Parse(hisSaveResult);
- errorCode = GetStr(hisSaveResultobj, "errorCode");
- errorMessage = GetStr(hisSaveResultobj, "errorMessage");
- if (errorCode == "")
- {
- errorCode = "USERPAYING"; // 轮询响应异常时保持轮询,避免误判失败
- WriteDiag("payONLINE", "轮询响应缺少 errorCode,原始返回: " + hisSaveResult);
- }
- if (errorCode != "USERPAYING")
- {
- break;
- }
- }
- if (errorCode == "USERPAYING")
- {
- errorCode = "-1";
- errorMessage = "支付超时!!!";
- }
- }
- }
- else
- {
- ///支付失败
- errorCode = code;
- errorMessage = msg;
- }
- }
- catch (Exception ex2)
- {
- errorCode = "-1";
- errorMessage = "ReportFormd:" + ex2.Message;
- //MessageBox.Show(errorMessage);
- }
- //loading.CloseLoading();
- if (loading != null && !loading.IsDisposed)
- {
- if (loading.InvokeRequired)
- loading.Invoke((MethodInvoker)delegate { loading.CloseLoading(); });
- else
- loading.CloseLoading();
- }
- }
- private JObject formSMFormd(SMForm formSM)
- {
- SMFormOutDataobj = new JObject();
- try
- {
- if (formSM.ShowDialog() == DialogResult.OK)
- {
- SMFormOutDataobj = formSM.OutjObject;
-
- }
- else
- {
- SMFormOutDataobj = formSM.OutjObject;
- }
- }
- catch (Exception ex2)
- {
- errorCode = "-1";
- errorMessage = "ReportFormd:" + ex2.Message;
- }
- return SMFormOutDataobj;
- }
- private void formPOSFormd(JObject InjObject)
- {
- try
- {
- string PayCode = ini.IniReadValue("DGPosPay", "PayCode");
- // 创建一个 JObject 对象
- JObject hisParamsobj = new JObject();
- // 创建一个 JArray 对象
- JArray paramsArray = new JArray();
- paramsArray.Add(InjObject);
- hisParamsobj["params"] = paramsArray;
- hisParamsobj["code"] = PayCode;
- hisParamsobj["session"] = inParamsobj["session"];
- //调用his进行数据Pos数据组织
- RestClient restClient = new RestClient();
- Task<string> task = restClient.HISHTTPServices(hisParamsobj.ToString(), "", "", "", RestClient.TimeoutQuery); //调用HIS回调
- string hisResult = task.Result;
- JObject hisResultObject = JObject.Parse(hisResult);
- // 20261020:同 payONLINE,改为安全取值,避免 HIS 返回非预期结构时空引用
- errorCode = GetStr(hisResultObject, "errorCode");
- errorMessage = GetStr(hisResultObject, "errorMessage");
- string callbackDatainfo = GetStr(hisResultObject, "callbackData");
- if (errorCode == "")
- {
- errorCode = "-1";
- errorMessage = "HIS返回非预期结构: " + hisResult;
- WriteDiag("formPOSFormd", "HIS 响应缺少 errorCode 字段,原始返回: " + hisResult);
- }
- if (errorCode == "0")
- {
- if (callbackDatainfo == "")
- {
- errorCode = "-1";
- errorMessage = "POS支付成功但未返回 callbackData,无法回写";
- WriteDiag("formPOSFormd", "缺少 callbackData,原始返回: " + hisResult);
- }
- else
- {
- //调用posdll
- DGPosPayClass dgPosPay = new DGPosPayClass();
- string dllResult = dgPosPay.DGPosPay(hisResult);
- JObject dllResultobj = JObject.Parse(dllResult);
- errorCode = GetStr(dllResultobj, "errorCode");
- errorMessage = GetStr(dllResultobj, "errorMessage");
- string SavePayCode = ini.IniReadValue("DGPosPay", "SavePayCode");
- JObject callbackDataobj = JObject.Parse(callbackDatainfo);
- callbackDataobj["code"] = SavePayCode;
- string paramsinfo = callbackDataobj["params"][0].ToString();
- JObject paramsobj = JObject.Parse(paramsinfo);
- paramsobj.Add("posData", dllResultobj);
- JArray paramsArray2 = new JArray();
- paramsArray2.Add(paramsobj);
- callbackDataobj["params"] = paramsArray2;
- Task<string> savetask = restClient.HISHTTPServices(callbackDataobj.ToString(), "", "", "", RestClient.TimeoutQuery); //调用HIS回调保存数据
- string hisSaveResult = savetask.Result;
- JObject hisSaveResultobj = JObject.Parse(hisSaveResult);
- errorCode = GetStr(hisSaveResultobj, "errorCode");
- errorMessage = GetStr(hisSaveResultobj, "errorMessage");
- string resultRaw = GetStr(hisSaveResultobj, "result");
- if (resultRaw != "") { result = JObject.Parse(resultRaw); }
- }
- }
- }
- catch (Exception ex2)
- {
- errorCode = "-1";
- errorMessage = "ReportFormd:" + ex2.Message;
- //MessageBox.Show(errorMessage);
- }
- //thread.Abort();
- }
- private JObject LoadingFormd()
- {
- try
- {
- loading.Show();
- }
- catch (Exception ex2)
- {
- loading = new Loading();
- loading.Show();
- }
- return SMFormOutDataobj;
- }
- }
- }
|