/******************************************************************************
* 文件名称: CardReader.cs
* 文件说明: 读卡的封装,包括社保卡,身份证,电子凭证等等
* 当前版本: V1.0
* 创建日期: 2022-06-20
*
* * 2020-06-20: 增加 CardReader 类
* ***** 2020-06-20: 增加 CardReader 方法,获取URL地址,USER信息,地方版SSCard.dll使用
* ***** 2020-06-20: 增加 CardReader 方法重载,国家版电子凭证使用
* ***** 2020-06-20: 增加 Init 方法,SSCard环境初始化
* ***** 2020-06-20: 增加 ReadCardBas 方法,读社保卡
* ***** 2020-06-20: 增加 VerifyPIN 方法,验证密码
* ***** 2020-06-20: 增加 ChangePIN 方法,修改密码
* ***** 2020-06-20: 增加 ReadSFZ 方法,读身份证
* ***** 2020-06-20: 增加 GetQRBase 方法,读二维码
* ***** 2020-06-20: 增加 NationEcTrans 方法,读电子凭证(国家版)
******************************************************************************/
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PTMedicalInsurance.Common.SMLib;
using PTMedicalInsurance.Entity;
using PTMedicalInsurance.Helper;
using PTMedicalInsurance.Variables;
using System;
using System.Runtime.InteropServices;
using System.Security.Policy;
using System.Text;
namespace PTMedicalInsurance.CardReaders
{
class CardReader
{
[DllImport("SSCard.dll", EntryPoint = "Init", CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
static extern int Init(IntPtr pURL, IntPtr pUser);
//[DllImport(DllPath, EntryPoint = "Init", CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
//static extern int Init(StringBuilder pInitInfo, StringBuilder pErrMsg);
[DllImport("SSCard.dll", EntryPoint = "ReadCardBas", CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
private static extern int ReadCardBas(IntPtr pOutBuff, int nOutBuffLen, IntPtr pSignBuff, int nSignBuffLen);
const string DllPath = @"SSCard.dll";
private static bool initilaized = false;
private static string URL;
private static string User;
public CardReader()
{
}
public CardReader(string URL, string User)
{
}
public static int Init(string _URL, string _User)
{
string errMsg = "";
int result = -1;
if (string.IsNullOrEmpty(_URL))
{
errMsg = "读卡地址未维护,请联系管理员!";
return result;
}
URL = _URL;
User = _User;
if (initilaized)
{
Global.writeLog("Init", "检测到已初始化", "");
return 0;
}
//Global.writeLog_debug(string.Format("读卡初始化:{0},{1}", URL, User));
// 将托管字符串转换为 ANSI 编码的字节数组
byte[] urlBytes = Encoding.ASCII.GetBytes(URL + '\0'); // 添加终止符
byte[] userBytes = Encoding.ASCII.GetBytes(User + '\0'); // 添加终止符
Global.writeLog("Init", URL, User);
// 分配全局非托管内存并复制字符串数据
IntPtr pUrl = Marshal.AllocHGlobal(urlBytes.Length);
IntPtr pUser = Marshal.AllocHGlobal(userBytes.Length);
try
{
// 将托管字节数组复制到非托管内存
Marshal.Copy(urlBytes, 0, pUrl, urlBytes.Length);
Marshal.Copy(userBytes, 0, pUser, userBytes.Length);
result = Init(pUrl, pUser);
if (result == 0)
{
initilaized = true;
}
return result;
}
catch (Exception ex)
{
errMsg = ex.Message;
return result;
}
finally
{
// 显式释放全局非托管内存
if (pUrl != IntPtr.Zero) Marshal.FreeHGlobal(pUrl);
if (pUser != IntPtr.Zero) Marshal.FreeHGlobal(pUser);
Global.writeLog($"Init:{_URL};{_User}:", result.ToString(), errMsg);
}
}
//public int Init()
//{
// string errMsg = "";
// int result = -1;
// try
// {
// StringBuilder sbInitInfo = new StringBuilder();
// StringBuilder sbUSER = new StringBuilder(4096);
// result = Init(sbInitInfo, sbUSER);
// return result;
// }
// catch (Exception ex)
// {
// errMsg = ex.Message;
// return result;
// }
// finally
// {
// Global.writeLog("初始化", "", result.ToString() + ":" + errMsg);
// }
//}
///
/// 读社保卡
///
///
///
public int ReadCardBas(out string basInfo)
{
basInfo = "";
int rtn = -1;
// 分配非托管内存
//IntPtr pOutBuff = Marshal.AllocCoTaskMem(1024);
//IntPtr pSignBuff = Marshal.AllocCoTaskMem(8192);
// 分配全局非托管内存
IntPtr pOutBuff = Marshal.AllocHGlobal(1024);
IntPtr pSignBuff = Marshal.AllocHGlobal(8192);
try
{
rtn = ReadCardBas(pOutBuff, 1024, pSignBuff, 8192);
if (rtn == 0)
{
// 解析返回读卡信息
string outString = Marshal.PtrToStringAnsi(pOutBuff);
string signString = Marshal.PtrToStringAnsi(pSignBuff);
basInfo = outString;
}
else
{
string outString = Marshal.PtrToStringAnsi(pOutBuff);
basInfo = outString;
}
return rtn;
}
catch (Exception ex)
{
basInfo = "ReadCardBas异常:" + ex.Message;
return rtn;
}
finally
{
Global.writeLog("读社保卡", "", basInfo);
//// 显式释放非托管内存
//Marshal.FreeCoTaskMem(pOutBuff);
//Marshal.FreeCoTaskMem(pSignBuff);
// 显式释放非托管内存
Marshal.FreeHGlobal(pOutBuff);
Marshal.FreeHGlobal(pSignBuff);
}
}
public int ReadCardBas(out string basInfo, out string signInfo)
{
basInfo = ""; signInfo = "";
int rtn = -1;
// 分配全局非托管内存
IntPtr pOutBuff = Marshal.AllocHGlobal(1024);
IntPtr pSignBuff = Marshal.AllocHGlobal(8192);
try
{
rtn = ReadCardBas(pOutBuff, 1024, pSignBuff, 8192);
if (rtn == 0)
{
// 解析返回读卡信息
string outString = Marshal.PtrToStringAnsi(pOutBuff);
string signString = Marshal.PtrToStringAnsi(pSignBuff);
basInfo = outString;
signInfo = signString;
}
else
{
string outString = Marshal.PtrToStringAnsi(pOutBuff);
string signString = Marshal.PtrToStringAnsi(pSignBuff);
basInfo = outString;
signInfo = signString;
}
return rtn;
}
catch (Exception ex)
{
basInfo = "ReadCardBas异常:" + ex.Message;
return rtn;
}
finally
{
// 显式释放非托管内存
Marshal.FreeHGlobal(pOutBuff);
Marshal.FreeHGlobal(pSignBuff);
Global.writeLog("ReadCardBas", basInfo, signInfo);
}
}
///
/// 读身份证
///
///
///
public int ReadSFZ(out string OutBuff)
{
OutBuff = "";
try
{
SFZReader reader = new SFZReader();
return reader.ReadSFZ(out OutBuff);
}
catch (Exception ex)
{
OutBuff = "ReadSFZ 异常:" + ex.Message;
return -1;
}
}
///
/// 6101
///
///
///
public int ReadEC(out string OutBuff)
{
OutBuff = "";
try
{
ECTokenData eCToken = new ECTokenData();
eCToken.orgId = Global.inf.hospitalNO;
eCToken.operatorId = Global.user.ID;
eCToken.operatorName = Global.user.name;
eCToken.officeId = Global.user.officeID;
eCToken.officeName = Global.user.officeName;
eCToken.businessType = Global.pat.card.ecBizType;
//eCToken.outBizNo = bizNo;
//eCToken.extData = "";
string tmp = JsonConvert.SerializeObject(eCToken);
var inData = new { InData = JObject.Parse(tmp) };
JObject joInData = new JObject(
new JProperty("InData", JObject.Parse(tmp))
);
ECReader reader = new ECReader();
return reader.Read(joInData,out OutBuff);
}
catch (Exception ex)
{
OutBuff = "ReadEC 异常:" + ex.Message;
return -1;
}
}
///
/// 6101
///
///
///
public int ECQuery(out string OutBuff)
{
OutBuff = "";
try
{
EcQueryData ecqueryData = new EcQueryData();
ecqueryData.orgId = Global.inf.hospitalNO;
ecqueryData.operatorId = Global.user.ID;
ecqueryData.operatorName = Global.user.name;
ecqueryData.officeId = Global.user.officeID;
ecqueryData.officeName = Global.user.officeName;
ecqueryData.businessType = Global.pat.card.ecBizType;
//eCToken.outBizNo = bizNo;
//eCToken.extData = "";
NationECInput nationECInput = new NationECInput();
nationECInput.data = ecqueryData;
nationECInput.orgId = Global.inf.hospitalNO;
nationECInput.transType = "ec.query";
nationECInput.extra = "";
NationECCodeAPI nationECCodeAPI = new NationECCodeAPI(Global.inf.ecURL);
return nationECCodeAPI.NationEcTrans(nationECInput, out OutBuff);
}
catch (Exception ex)
{
OutBuff = "ECQuery 异常:" + ex.Message;
return -1;
}
}
///
/// 人脸识别
///
///
///
public int FaceRecognition(out string OutBuff)
{
Random rd = new Random();
int iNum = rd.Next();
Global.pat.card.outBizNo = DateTime.Now.ToString("YYYYMMDDHHNNSS") + Global.inf.hospitalNO + iNum.ToString().Substring(0, 5);
EcAuthData ecAuthData = new EcAuthData();
ecAuthData.orgId = Global.inf.hospitalNO;
ecAuthData.operatorId = Global.user.ID;
ecAuthData.operatorName = Global.user.name;
ecAuthData.officeId = Global.user.officeID;
ecAuthData.officeName = Global.user.officeName;
ecAuthData.businessType = Global.pat.card.ecBizType;
ecAuthData.outBizNo = DateTime.Now.ToString("YYYYMMDDHHNNSS") + Global.inf.hospitalNO + iNum.ToString().Substring(0, 5);
NationECInput nationECInput = new NationECInput();
nationECInput.data = ecAuthData;
nationECInput.orgId = Global.inf.hospitalNO;
nationECInput.transType = "cn.nhsa.ec.auth";
nationECInput.extra = "";
//调用刷脸认证接口cn.nhsa.ec.auth返回授权信息authNo
NationECCodeAPI nationECCodeAPI = new NationECCodeAPI(Global.inf.ecURL);
nationECCodeAPI.NationEcTrans(nationECInput, out OutBuff);
if (JsonHelper.getDestValue(JObject.Parse(OutBuff), "code") != "0")
{
OutBuff = $"调用刷脸认证接口{nationECInput.transType}失败:" + OutBuff;
return -1;
}
else
{
//成功返回授权信息
string auth = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.authNo");
AuthCheckData authCheckData = new AuthCheckData();
authCheckData.orgId = Global.inf.hospitalNO;
authCheckData.operatorId = Global.user.ID;
authCheckData.operatorName = Global.user.name;
authCheckData.officeId = Global.user.officeID;
authCheckData.officeName = Global.user.officeName;
authCheckData.businessType = Global.pat.card.ecBizType ?? "01102";
Global.pat.card.outBizNo = DateTime.Now.ToString("YYYYMMDDHHNNSS") + Global.inf.hospitalNO + iNum.ToString().Substring(0, 5);
authCheckData.outBizNo = Global.pat.card.outBizNo;
authCheckData.authNo = auth;
nationECInput = new NationECInput();
nationECInput.data = authCheckData;
nationECInput.orgId = Global.inf.hospitalNO;
nationECInput.transType = "cn.nhsa.auth.check";
//2.调用刷脸认证接口cn.nhsa.ec.auth返回授权信息authNo
nationECCodeAPI.NationEcTrans(nationECInput, out OutBuff);
if (JsonHelper.getDestValue(JObject.Parse(OutBuff), "code") != "0")
{
OutBuff = $"调用刷脸认证接口{nationECInput.transType}失败:" + OutBuff;
return -1;
}
else
{
Global.pat.ecToken = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.ecToken");
Global.pat.name = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.userName");
Global.pat.IDType = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.idType");
Global.pat.IDNO = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.idNo");
Global.pat.insuplc_admdvs = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.insuOrg");
Global.pat.mdtrtcertNO = Global.pat.ecToken;
Global.pat.certType = Global.pat.IDType;
Global.pat.certNO = Global.pat.IDNO;
return 0;
}
}
}
///
/// 结算完成后,传入人脸识别时的outBizNo,以及结算信息
///
///
///
///
public SettleNotifyData GetSettleNotifyData(JObject joIn,JObject joSettlementRtn)
{
SettleNotifyData data = new SettleNotifyData();
data.outBizNo = JsonHelper.getDestValue(joIn, "outBizNo"); // 核身或者刷脸的医疗机构业务流水号(必填)
data.medicalSettleState = JsonHelper.getDestValue(joIn, "medicalSettleState"); // 医保/自费结算状态(必填)
data.authNo = JsonHelper.getDestValue(joIn, "authNo"); // 实人认证业务流水号(可空)
data.totalFee = JsonHelper.getDestValue(joIn, "medfee_sumamt"); // 总费用(必填)字符型(金额建议为分或保留小数)
data.bizType = JsonHelper.getDestValue(joIn, "bizType"); // 业务场景(必填)
data.idNo = JsonHelper.getDestValue(joIn, "idNo"); // 身份证号码(可空)
data.userName = JsonHelper.getDestValue(joIn, "userName"); // 姓名(可空)
data.setlTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") ; // 结算时间(必填)格式如:yyyy-MM-dd HH:mm:ss
data.hospitalName = Global.inf.hospitalNO; // 定点机构名称(必填)
data.doctorName = JsonHelper.getDestValue(joIn, "doctorName"); // 医生姓名(可空)
data.failReason = JsonHelper.getDestValue(joIn, "failReason"); // 失败原因(可空)
// ========== 以下字段在 medicalSettleState=SUCCESS 或 REFUND_SUCCESS 时必填 ==========
if (data.medicalSettleState.Contains("SUCCESS"))
{
data.medicalSettleNo = JsonHelper.getDestValue(joSettlementRtn, "setl_id"); // 医保单据流水号(成功时必填)
//data.ownAmt = JsonHelper.getDestValue(joSettlementRtn, "fulamt_ownpay_amt"); // 自费费用(成功时必填)
//data.hifAmt = JsonHelper.getDestValue(joSettlementRtn, "hifp_pay"); // 医保报销费用(成功时必填) ??各项相加?
data.ownAmt = Global.Set.psnCashAmt.ToString(); // 自费费用(成功时必填)
data.hifAmt = (Global.Set.psnAcctAmt + Global.Set.ybAmt + Global.Set.hospAmt).ToString(); // 三笔费用相加
data.acctAmt = JsonHelper.getDestValue(joSettlementRtn, "acct_pay"); // 个人帐户支出(成功时必填)
data.hifpAmt = JsonHelper.getDestValue(joSettlementRtn, "hifp_pay"); // 统筹基金支出(成功时必填)
data.hifmiAmt = JsonHelper.getDestValue(joSettlementRtn, "hifob_pay"); // 大额医疗保险支出(可空)
data.cvlservAmt = JsonHelper.getDestValue(joSettlementRtn, "cvlserv_pay"); // 公务员补助(可空)
data.maAmt = JsonHelper.getDestValue(joSettlementRtn, "maf_pay"); // 医疗救助(可空)
data.hosPreAmt = JsonHelper.getDestValue(joSettlementRtn, "hosp_part_amt"); // 单病种定点医疗机构垫支(可空)
data.medOverLmtAmt = JsonHelper.getDestValue(joSettlementRtn, "overlmt_selfpay"); // 药品超标扣款金额(可空)
data.mafAmt = ""; // 扶贫救助(可空)
data.cvlservDedcAmt = ""; // 历史起付公务员返还(可空)
data.balance = JsonHelper.getDestValue(joSettlementRtn, "balc"); // 帐户余额(可空)
}
return data;
}
public int SettleNotify(SettleNotifyData settleNotifyData, out string OutBuff)
{
settleNotifyData.orgId = Global.inf.hospitalNO;
settleNotifyData.operatorId = Global.user.ID;
settleNotifyData.operatorName = Global.user.name;
settleNotifyData.officeId = Global.user.officeID;
settleNotifyData.officeName = Global.user.officeName;
settleNotifyData.businessType = Global.pat.card.ecBizType;
NationECInput nationECInput = new NationECInput();
nationECInput.data = settleNotifyData;
nationECInput.orgId = Global.inf.hospitalNO;
nationECInput.transType = "cn.nhsa.settle.notify";
nationECInput.extra = "";
//调用刷脸认证接口cn.nhsa.ec.auth返回授权信息authNo
NationECCodeAPI nationECCodeAPI = new NationECCodeAPI(Global.inf.ecURL);
nationECCodeAPI.NationEcTrans(nationECInput, out OutBuff);
if (JsonHelper.getDestValue(JObject.Parse(OutBuff), "code") != "0")
{
OutBuff = $"调用刷脸认证接口{nationECInput.transType}失败:" + OutBuff;
return -1;
}
else
{
return 0;
}
}
///
/// 【1002】费用结算支付密码校验
///
/// 明文密码
/// 错误信息
/// 0成功,其他失败
public int VerifyPin(string password, out string errMsg)
{
errMsg = "";
int result = -1;
string input = password;
try
{
// 使用全局变量获取参数
string mdtrtCertNo = Global.pat.mdtrtcertNO; // 就诊凭证编号
string mdtrtCertType = Global.pat.mdtrtcertType; // 就诊凭证类型
string certNo = Global.pat.certNO; // 证件号码
string psnCertType = Global.pat.certType; // 人员证件类型
string psnNo = Global.pat.psn_no; // 人员编号
// 获取医药机构秘钥(需要先初始化)
Global.inf.secretKey = "DDF0EBD542B19072";//银海控件的密钥
string secretKey = VerifyPasswordEncryptUtils.encrypt(Global.inf.secretKey, Global.inf.secretKey);
string cipherText = VerifyPasswordEncryptUtils.encrypt(password, Global.inf.secretKey);
// SM4加密密码
//string cipherText = PTMedicalInsurance.Common.SignUtils.sm4Encrypt(
// Global.inf.appId, secretKey, password);
// 构建输入参数
JObject joData = new JObject();
joData["mdtrt_cert_no"] = mdtrtCertNo;
joData["mdtrt_cert_type"] = mdtrtCertType;
joData["certno"] = certNo;
joData["psn_cert_type"] = psnCertType;
joData["psn_no"] = psnNo;
joData["secret_key"] = secretKey;
joData["ciphertext"] = cipherText;
input = joData.ToString();
// 调用COM组件
InvokeComCenter2026 carder = new InvokeComCenter2026();
result = carder.Business("1002", ref input, ref errMsg);
if (result != 0) return result;
JObject joRtn = JObject.Parse(errMsg);
result = JsonHelper.parseComRtnValue(joRtn, out errMsg);
return result;
}
catch (Exception ex)
{
errMsg = "VerifyPin异常:" + ex.Message;
return result;
}
finally
{
Global.writeLog($"VerifyPin:{result}", input, errMsg);
}
}
}
}