|
|
using DSWeb.EntityDA;
|
|
|
using Newtonsoft.Json;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.IO;
|
|
|
using System.Net;
|
|
|
using System.Net.Security;
|
|
|
using System.Security.Cryptography;
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
using System.Text;
|
|
|
using System.Web;
|
|
|
using System.Linq;
|
|
|
using System.Text.RegularExpressions;
|
|
|
using log4net;
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
using DSWeb.Areas.Account.DAL.Chfee_Invoice_HangXin;
|
|
|
using DSWeb.MvcShipping.DAL.MsCompanysDAL;
|
|
|
using DSWeb.MvcShipping.DAL.MsSysParamSet;
|
|
|
using DSWeb.Areas.CommMng.DAL;
|
|
|
|
|
|
namespace DSWeb.Common.Helper
|
|
|
{
|
|
|
public class RuihongHelper
|
|
|
{
|
|
|
private const string apiUrlTest = "https://www.chinaeinv.com:943/igs/api/invoiceApi.jspa";//测试环境
|
|
|
private const string apiUrl = "https://www.chinaeinv.com/igs/api/invoiceApi.jspa";//正式环境
|
|
|
|
|
|
|
|
|
public RuihongHelper()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 开启测试模式(使用测试环境)
|
|
|
/// </summary>
|
|
|
private bool IsTestMode = false;
|
|
|
|
|
|
#region 对象属性
|
|
|
|
|
|
private string AppCode
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
if (IsTestMode)
|
|
|
{
|
|
|
return "PTTEST17";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return "PT000147";
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private string KeyStore
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
if (IsTestMode)
|
|
|
{
|
|
|
return "PTTEST17.pfx";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return "PT000147.pfx";
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private string KeyStorePsw
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
if (IsTestMode)
|
|
|
{
|
|
|
return "PTTEST17";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return "vfrsedazsed";
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private string ApiUrl
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
if (IsTestMode)
|
|
|
{
|
|
|
return apiUrlTest;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return apiUrl;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 公共方法
|
|
|
#region 开票
|
|
|
/// <summary>
|
|
|
/// 开票
|
|
|
/// </summary>
|
|
|
/// <param name="strParams"></param>
|
|
|
/// <returns></returns>
|
|
|
public string PostInvoice(string strParams)
|
|
|
{
|
|
|
BasicDataRefDAL.SaveLog($"{strParams}","", "瑞宏开票", "发出");
|
|
|
var rtn = DoPostData(strParams, "chinaeinv.api.invoice.v3.kp_async");
|
|
|
BasicDataRefDAL.SaveLog($"{rtn}", "", "瑞宏开票", "返回");
|
|
|
|
|
|
return rtn;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 开票
|
|
|
/// </summary>
|
|
|
/// <param name="strParams"></param>
|
|
|
/// <returns></returns>
|
|
|
public string PostInvoice(RuihongPostModel postModel)
|
|
|
{
|
|
|
var strJson = JsonConvert.SerializeObject(postModel);
|
|
|
if (postModel.notices == null || postModel.notices.Count == 0)
|
|
|
{
|
|
|
JObject jobj = JObject.Parse(strJson);
|
|
|
jobj.Remove("notices");
|
|
|
strJson = jobj.ToString();
|
|
|
}
|
|
|
return PostInvoice(strJson);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 开票
|
|
|
/// </summary>
|
|
|
/// <param name="invId"></param>
|
|
|
/// <returns></returns>
|
|
|
public bool PostInvoiceRecord(string invId, out string msg)
|
|
|
{
|
|
|
var inv = ChinvoiceDAL.GetData("cm.GID='"+ invId + "'");
|
|
|
var invItems = ChinvoiceDAL.GetDetailList("PID='" + invId + "'");
|
|
|
|
|
|
|
|
|
RuihongPostModel postModel = new RuihongPostModel();
|
|
|
postModel.serialNo = inv.GID;
|
|
|
postModel.postTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
|
postModel.order = new RuihongPostOrderModel() { orderNo = inv.GID };
|
|
|
if (inv.RECVCURR=="USD")
|
|
|
{
|
|
|
postModel.dynamicParams = new { dollarAmount = inv.OTCURRAMOUNT.ToString() };
|
|
|
}
|
|
|
|
|
|
RuihongPostInvoiceModel postInv = new RuihongPostInvoiceModel();
|
|
|
if (IsTestMode)
|
|
|
{
|
|
|
postInv.taxpayerCode = "91370200264807TEST4A";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
//var company = MsCompanysDAL.GetNoPicData("GID='" + inv.COMPANYID + "'");
|
|
|
postInv.taxpayerCode = inv.TAXCODE;
|
|
|
postInv.taxpayerName = inv.BILLRISES;
|
|
|
postInv.taxpayerAddress = inv.ADDRESS;
|
|
|
postInv.taxpayerTel = inv.OFFICEPHONE;
|
|
|
}
|
|
|
if (!string.IsNullOrEmpty(inv.PUSHEMAIL)) {
|
|
|
postModel.notices = new List<RuihongPostNoticeModel>();
|
|
|
postModel.notices.Add(new RuihongPostNoticeModel()
|
|
|
{
|
|
|
type = "email",
|
|
|
value= inv.PUSHEMAIL
|
|
|
});
|
|
|
}
|
|
|
|
|
|
//分拆银行和账号
|
|
|
for (int idx = inv.ACCOUNT.Length - 1; idx >= 0; idx--)
|
|
|
{
|
|
|
var idxStr = inv.ACCOUNT[idx].ToString();
|
|
|
if (Regex.IsMatch(idxStr, "[0-9\\s-]{1}"))
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
postInv.taxpayerBankName = inv.ACCOUNT.Substring(0, idx + 1);
|
|
|
postInv.taxpayerBankAccount = inv.ACCOUNT.Substring(idx + 1);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
postInv.customerCode = inv.CUSTRATENO;
|
|
|
postInv.customerName = inv.INVOICECUSTNAME;
|
|
|
postInv.customerAddress = inv.CUSTADDR;
|
|
|
postInv.customerTel = inv.CUSTTEL;
|
|
|
|
|
|
//分拆银行和账号
|
|
|
for (int idx = inv.CUSTBANK.Length - 1; idx >= 0; idx--)
|
|
|
{
|
|
|
var idxStr = inv.CUSTBANK[idx].ToString();
|
|
|
if (Regex.IsMatch(idxStr, "[0-9\\s-]{1}"))
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
postInv.customerBankName = inv.CUSTBANK.Substring(0, idx + 1);
|
|
|
postInv.customerBankAccount = inv.CUSTBANK.Substring(idx + 1);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
postInv.drawer = inv.OPERATORNAME;
|
|
|
postInv.reviewer = inv.CHECKER;
|
|
|
postInv.payee = inv.PAYEE;
|
|
|
postInv.totalAmount = Math.Round(inv.INVAMOUNT, 2, MidpointRounding.AwayFromZero).ToString("#0.00");
|
|
|
postInv.remark = inv.REMARK;
|
|
|
postModel.invoice = postInv;
|
|
|
|
|
|
postInv.items = new List<RuihongPostInvoiceItemModel>();
|
|
|
foreach (var item in invItems)
|
|
|
{
|
|
|
var goodsCode = item.GOODCODE == null ? string.Empty : item.GOODCODE;
|
|
|
goodsCode = goodsCode.PadRight(19, '0');
|
|
|
|
|
|
|
|
|
//零税率标识。1:免税,2:不征税,3:普通零税率。税率为零的情况下,如果不传,则默认为1:免税。
|
|
|
var 零税率标识 = "1";
|
|
|
|
|
|
if (item.TAXRATE > 0)
|
|
|
{
|
|
|
零税率标识 = null;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
//if (item.ZTAXTYPE == "免税") 零税率标识 = "1";
|
|
|
//if (item.ZTAXTYPE == "不征税") 零税率标识 = "2";
|
|
|
//if (item.ZTAXTYPE == "普通零税率") 零税率标识 = "3";
|
|
|
零税率标识= item.ZTAXTYPE ;
|
|
|
}
|
|
|
var taxrate = Math.Round(item.TAXRATE / 100, 2, MidpointRounding.AwayFromZero).ToString();
|
|
|
if (taxrate == "0.00" || taxrate == "0.0") taxrate = "0";
|
|
|
|
|
|
|
|
|
postInv.items.Add(new RuihongPostInvoiceItemModel()
|
|
|
{
|
|
|
type = "0",
|
|
|
name = item.GOODSNAMEREF,
|
|
|
spec = item.SPEC,
|
|
|
uom = item.UNIT,
|
|
|
quantity = item.PKGS,
|
|
|
taxRate = taxrate,
|
|
|
amount = Math.Round(item.AMOUNT + item.TAX, 2, MidpointRounding.AwayFromZero).ToString("#0.00"),
|
|
|
price = item.PRICE,
|
|
|
catalogCode = goodsCode,
|
|
|
preferentialPolicyFlg = item.ISUSEPREF,
|
|
|
addedValueTaxFlg = item.DEFREMARK,
|
|
|
zeroTaxRateFlg = 零税率标识
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string rtn = PostInvoice(postModel);
|
|
|
var rtnObj = JsonConvert.DeserializeObject<RuihongRespCommon>(rtn);
|
|
|
if (rtnObj.code == "0")
|
|
|
{
|
|
|
T_ALL_DA T_ALL_DA = new EntityDA.T_ALL_DA();
|
|
|
var blUpSQL = "update ch_fee_invoice set INVOICESERIALNUM='" + rtnObj.serialNo + "',EINVOICESTATE='1' where GID='" + invId + "' ";
|
|
|
bool bl = T_ALL_DA.GetExecuteSqlCommand(blUpSQL);
|
|
|
|
|
|
//inv.SerialNo = rtnObj.serialNo;
|
|
|
//inv.Status = InvoiceRecord.StatusPost;
|
|
|
//invDB.SaveChanges();
|
|
|
|
|
|
msg = rtnObj.message;
|
|
|
return true;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
T_ALL_DA T_ALL_DA = new EntityDA.T_ALL_DA();
|
|
|
var blUpSQL = "update ch_fee_invoice set EINVOICESTATE='1' where GID='" + invId + "' ";
|
|
|
bool bl = T_ALL_DA.GetExecuteSqlCommand(blUpSQL);
|
|
|
|
|
|
msg = rtnObj.message;
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 查票
|
|
|
/// <summary>
|
|
|
/// 查票
|
|
|
/// </summary>
|
|
|
/// <param name="strParams"></param>
|
|
|
/// <returns></returns>
|
|
|
public string QueryInvoice(string strParams)
|
|
|
{
|
|
|
BasicDataRefDAL.SaveLog($"{strParams}", "", "瑞宏查票", "发出");
|
|
|
//logger.Debug($"瑞宏查票:{strParams}");
|
|
|
var rtn = DoPostData(strParams, "chinaeinv.api.invoice.v3.cx");
|
|
|
BasicDataRefDAL.SaveLog($"{rtn}", "", "瑞宏查票返回", "返回");
|
|
|
//logger.Debug($"瑞宏查票返回:{rtn}");
|
|
|
return rtn;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 根据操作流水号单个查询
|
|
|
/// </summary>
|
|
|
/// <param name="strParams">单个操作流水号</param>
|
|
|
/// <returns></returns>
|
|
|
public string QueryInvoiceSerialNo(params string[] serNO)
|
|
|
{
|
|
|
if (serNO == null || serNO.Length == 0)
|
|
|
{
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
RuihongQueryModel reqModel = new RuihongQueryModel();
|
|
|
reqModel.serialNo = Guid.NewGuid().ToString();
|
|
|
reqModel.postTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
|
reqModel.criteria = new List<RuihongQueryInner>();
|
|
|
foreach (var item in serNO)
|
|
|
{
|
|
|
reqModel.criteria.Add(new RuihongQueryInner()
|
|
|
{
|
|
|
name = "singlesSerialNo",
|
|
|
value = item
|
|
|
});
|
|
|
}
|
|
|
|
|
|
return QueryInvoice(JsonConvert.SerializeObject(reqModel));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 查询发票开出
|
|
|
/// </summary>
|
|
|
/// <param name="invId"></param>
|
|
|
/// <param name="msg"></param>
|
|
|
/// <returns></returns>
|
|
|
public bool QueryInvoiceRecord(string invId,string INVOICESERIALNUM, out string msg)
|
|
|
{
|
|
|
//var inv = ChinvoiceDAL.GetData("cm.GID='" + invId + "'");
|
|
|
if (string.IsNullOrEmpty(INVOICESERIALNUM))
|
|
|
{
|
|
|
msg = "还未成功进行平台开票,无可读数据";
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
string rtn = QueryInvoiceSerialNo(INVOICESERIALNUM);
|
|
|
var rtnObj = JsonConvert.DeserializeObject<RuihongRespQuery>(rtn);
|
|
|
if (rtnObj.code == "0")
|
|
|
{
|
|
|
var InvCode = rtnObj.invoices[0].code.Substring(0, 12);
|
|
|
var InvNum = rtnObj.invoices[0].code.Substring(12);
|
|
|
var PdfUrl = rtnObj.invoices[0].pdfUnsignedUrl;
|
|
|
|
|
|
PdfUrl = PdfUrl.Replace("Unsigned","");
|
|
|
|
|
|
var PictureUrl = rtnObj.invoices[0].viewUrl;
|
|
|
var InvoiceDate = Convert.ToDateTime(rtnObj.invoices[0].generateTime).ToString("yyyy-MM-dd");
|
|
|
var needautolock = MsSysParamSetDAL.GetSysParam("INVOICEAUTOLOCK").isnullortrue();
|
|
|
T_ALL_DA T_ALL_DA = new EntityDA.T_ALL_DA();
|
|
|
var blUpSQL = "update ch_fee_invoice set INVOICECODE='" + InvCode + "',INVOICENO='" + InvNum + "',INVOICEINFOURL='" + PictureUrl + "',INVOICEPDFURL='" + PdfUrl + "',INVOICEMAKETIME='" + InvoiceDate + "',EINVOICESTATE='2' where GID='" + invId + "' ";
|
|
|
if (needautolock)
|
|
|
{
|
|
|
blUpSQL = "update ch_fee_invoice set BILLSTATUS=1,INVOICECODE='" + InvCode + "',INVOICENO='" + InvNum + "',INVOICEINFOURL='" + PictureUrl + "',INVOICEPDFURL='" + PdfUrl + "',INVOICEMAKETIME='" + InvoiceDate + "',EINVOICESTATE='2' where GID='" + invId + "' ";
|
|
|
}
|
|
|
bool bl = T_ALL_DA.GetExecuteSqlCommand(blUpSQL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//读取成功后,发送邮件
|
|
|
//if (inv.PushMode == "0" && !string.IsNullOrEmpty(inv.Email))
|
|
|
//{
|
|
|
// MailHelper.SendMailInvoiceRecord(inv.GID);
|
|
|
//}
|
|
|
|
|
|
msg = rtnObj.message;
|
|
|
return true;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
msg = rtnObj.message;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
#region 冲红
|
|
|
/// <summary>
|
|
|
/// 冲红
|
|
|
/// </summary>
|
|
|
/// <param name="strParams"></param>
|
|
|
/// <returns></returns>
|
|
|
public string RedInvoice(string strParams)
|
|
|
{
|
|
|
BasicDataRefDAL.SaveLog($"{strParams}", "", "瑞宏冲红", "发出");
|
|
|
var rtn = DoPostData(strParams, "chinaeinv.api.invoice.v3.ch_async");
|
|
|
BasicDataRefDAL.SaveLog($"{rtn}", "", "瑞宏冲红返回", "返回");
|
|
|
|
|
|
return rtn;
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 冲红
|
|
|
/// </summary>
|
|
|
/// <param name="redModel"></param>
|
|
|
/// <returns></returns>
|
|
|
public string RedInvoice(RuihongRedInvoiceModel redModel)
|
|
|
{
|
|
|
return RedInvoice(JsonConvert.SerializeObject(redModel));
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 冲红
|
|
|
/// </summary>
|
|
|
/// <param name="invId"></param>
|
|
|
/// <returns></returns>
|
|
|
public bool RedInvoiceRecord(string invId, out string msg)
|
|
|
{
|
|
|
var inv = ChinvoiceDAL.GetData("cm.GID='" + invId + "'");
|
|
|
//var invItems = ChinvoiceDAL.GetDetailList("PID='" + invId + "'");
|
|
|
RuihongRedInvoiceModel redModel = new RuihongRedInvoiceModel();
|
|
|
redModel.serialNo = inv.GID;
|
|
|
redModel.postTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
|
redModel.originalCode = inv.REDCODE + inv.REDNUM;
|
|
|
redModel.reason = "开票有误";
|
|
|
|
|
|
|
|
|
string rtn = RedInvoice(redModel);
|
|
|
var rtnObj = JsonConvert.DeserializeObject<RuihongRespCommon>(rtn);
|
|
|
if (rtnObj.code == "0")
|
|
|
{
|
|
|
//inv.SerialNo = rtnObj.serialNo;
|
|
|
//inv.Status = InvoiceRecord.StatusPost;
|
|
|
//invDB.SaveChanges();
|
|
|
T_ALL_DA T_ALL_DA = new EntityDA.T_ALL_DA();
|
|
|
var blUpSQL = "update ch_fee_invoice set INVOICESERIALNUM='" + rtnObj.serialNo + "',EINVOICESTATE='2' where GID='" + invId + "' ";
|
|
|
bool bl = T_ALL_DA.GetExecuteSqlCommand(blUpSQL);
|
|
|
msg = rtnObj.message;
|
|
|
return true;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
msg = rtnObj.message;
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
public string DoPostData(string kpParams, string cmd)
|
|
|
{
|
|
|
//appCode
|
|
|
string appcode = UrlEncode(AppCode, Encoding.UTF8);
|
|
|
//cmdName
|
|
|
string cmdName = UrlEncode(cmd, Encoding.UTF8);
|
|
|
//sign
|
|
|
string sign = UrlEncode(GetSign(kpParams), Encoding.UTF8);
|
|
|
//URL
|
|
|
StringBuilder Url = new StringBuilder();
|
|
|
Url.Append(ApiUrl);
|
|
|
Url.Append("?sign=" + sign);
|
|
|
Url.Append("&cmdName=" + cmdName);
|
|
|
Url.Append("&appCode=" + appcode);
|
|
|
|
|
|
//Post 请求URL
|
|
|
HttpWebRequest request = null;
|
|
|
HttpWebResponse response = null;
|
|
|
try
|
|
|
{
|
|
|
//设置最大连接数
|
|
|
ServicePointManager.DefaultConnectionLimit = 200;
|
|
|
//设置https验证方式
|
|
|
if (ApiUrl.StartsWith("https", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
ServicePointManager.ServerCertificateValidationCallback =
|
|
|
new RemoteCertificateValidationCallback(CheckValidationResult);
|
|
|
}
|
|
|
request = WebRequest.Create(Url.ToString()) as HttpWebRequest;
|
|
|
byte[] requestBytes = Encoding.GetEncoding("UTF-8").GetBytes(kpParams);
|
|
|
request.Method = "POST";
|
|
|
request.Timeout = 180 * 1000;
|
|
|
request.ContentType = "application/json;charset=UTF-8";
|
|
|
request.ContentLength = requestBytes.Length;
|
|
|
using (Stream requestStream = request.GetRequestStream())
|
|
|
{
|
|
|
requestStream.Write(requestBytes, 0, requestBytes.Length);
|
|
|
requestStream.Close();
|
|
|
}
|
|
|
//获取响应报文
|
|
|
response = request.GetResponse() as HttpWebResponse;
|
|
|
using (StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream(), Encoding.GetEncoding("UTF-8")))
|
|
|
{
|
|
|
return reader.ReadToEnd().Trim();
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
return ex.Message;
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
//关闭连接和流
|
|
|
if (response != null)
|
|
|
{
|
|
|
response.Close();
|
|
|
}
|
|
|
if (request != null)
|
|
|
{
|
|
|
request.Abort();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 私有方法
|
|
|
private bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// UrlEncode
|
|
|
/// </summary>
|
|
|
/// <param name="temp">要转码的文本</param>
|
|
|
/// <param name="encoding">编码</param>
|
|
|
/// <returns></returns>
|
|
|
private string UrlEncode(string temp, Encoding encoding)
|
|
|
{
|
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
|
for (int i = 0; i < temp.Length; i++)
|
|
|
{
|
|
|
string t = temp[i].ToString();
|
|
|
string k = HttpUtility.UrlEncode(t, encoding);
|
|
|
if (t == k)
|
|
|
{
|
|
|
stringBuilder.Append(t);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
stringBuilder.Append(k.ToUpper());
|
|
|
}
|
|
|
}
|
|
|
return stringBuilder.ToString();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 数字签名
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
private string GetSign(string kpParams)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
var keyStorePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, KeyStore);
|
|
|
//logger.Debug($"keyStorePath:{keyStorePath}");
|
|
|
//logger.Debug($"KeyStorePsw:{KeyStorePsw}");
|
|
|
X509Certificate2 x = new X509Certificate2(keyStorePath, KeyStorePsw, X509KeyStorageFlags.Exportable);
|
|
|
RSACryptoServiceProvider rsa = x.PrivateKey as RSACryptoServiceProvider;
|
|
|
byte[] data = Encoding.UTF8.GetBytes(kpParams);
|
|
|
RSACryptoServiceProvider privateKey1 = new RSACryptoServiceProvider();
|
|
|
privateKey1.ImportParameters(rsa.ExportParameters(true));
|
|
|
byte[] sign = privateKey1.SignData(data, "MD5");
|
|
|
string strSign = Convert.ToBase64String(sign);
|
|
|
string s1 = strSign.Substring(0, 76);
|
|
|
string s2 = strSign.Substring(76 * 1, 76);
|
|
|
string s3 = strSign.Substring(76 * 2);
|
|
|
string resultSign = UrlEncode(s1, Encoding.UTF8) + "%0D%0A"
|
|
|
+ UrlEncode(s2, Encoding.UTF8) + "%0D%0A"
|
|
|
+ UrlEncode(s3, Encoding.UTF8);
|
|
|
return resultSign;
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
var excep = ex;
|
|
|
while (excep != null)
|
|
|
{
|
|
|
//logger.Error(excep.Message);
|
|
|
//logger.Error(excep.StackTrace);
|
|
|
excep = excep.InnerException;
|
|
|
}
|
|
|
return string.Empty;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|
|
|
|
|
|
|
|
|
#region 交互对象
|
|
|
#region 开票请求
|
|
|
public class RuihongPostModel
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 是 50 操作流水号。传入重复的操作流水号则认为是重复操作。
|
|
|
/// </summary>
|
|
|
public string serialNo { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 19 请求发送时间。格式为yyyy-MM-dd HH:mm:ss。
|
|
|
/// </summary>
|
|
|
public string postTime { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 订单信息。
|
|
|
/// </summary>
|
|
|
public RuihongPostOrderModel order { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 发票信息。
|
|
|
/// </summary>
|
|
|
public RuihongPostInvoiceModel invoice { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 3 通知方式列表,最多3条。
|
|
|
/// </summary>
|
|
|
public List<RuihongPostNoticeModel> notices { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 20 扩展参数。一组Key-Value形式的数据,会在响应报文中回传给调用方,由调用者和瑞宏网双方根据实际情况协商使用。
|
|
|
/// </summary>
|
|
|
public object extendedParams { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 自定义参数。一组Key-Value形式的数据,key值:callbackUrl,Value为回调地址“http://wwww.***.com/test/invoice”
|
|
|
/// </summary>
|
|
|
public object dynamicParams { get; set; }
|
|
|
}
|
|
|
|
|
|
public class RuihongPostOrderModel
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 是 50 订单编号。
|
|
|
/// </summary>
|
|
|
public string orderNo { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 100 消费者的用户名。
|
|
|
/// </summary>
|
|
|
public string account { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 250 货物配送地址。
|
|
|
/// </summary>
|
|
|
public string address { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 消费者电话号码。手机号码或区号-固定电话号码。
|
|
|
/// </summary>
|
|
|
public string tel { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 消费者电子邮件地址。电子邮件格式。
|
|
|
/// </summary>
|
|
|
public string email { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
public class RuihongPostInvoiceItemModel
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 是 1 发票行性质 0 正常行、1 折扣行、2 被 折扣行。
|
|
|
/// </summary>
|
|
|
public string type { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 50 商品编码。
|
|
|
/// </summary>
|
|
|
public string code { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 90 商品名称。可在每一行商品下加入折扣行,折扣行商品名称与被折扣商品名称一致,金额和税额栏以负数填写,税率与被折扣行商品税率相同,其它栏不填写。
|
|
|
/// </summary>
|
|
|
public string name { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 40 规格型号。
|
|
|
/// </summary>
|
|
|
public string spec { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 两者要么都为空,要么都不为空。 18(整)6(小) 商品单价。必须等于amount/quantity的四舍五入值。
|
|
|
/// </summary>
|
|
|
public decimal price { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 两者要么都为空,要么都不为空。 18(整)6(小) 数量。必须大于等于0.000001。
|
|
|
/// </summary>
|
|
|
public decimal quantity { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 20 单位。
|
|
|
/// </summary>
|
|
|
public string uom { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 6(小) 税率。只能为0、 0.03、0.04、0.06、0.10、0.11、0.16、0.17。
|
|
|
/// </summary>
|
|
|
public string taxRate { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 18(整)2(小) 税价合计金额。
|
|
|
/// </summary>
|
|
|
public string amount { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 50 商品分类编码。目前的分类编码为19位,不足19位的在后面补0。
|
|
|
/// </summary>
|
|
|
public string catalogCode { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 1 优惠政策标识。0:不使用,1:使用。
|
|
|
/// </summary>
|
|
|
public string preferentialPolicyFlg { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 50 增值税特殊管理(当优惠政策标识为1时必填)。
|
|
|
/// </summary>
|
|
|
public string addedValueTaxFlg { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 1 零税率标识。1:免税,2:不征税,3:普通零税率。税率为零的情况下,如果不传,则默认为3:普通零税率。
|
|
|
/// </summary>
|
|
|
public string zeroTaxRateFlg { get; set; }
|
|
|
}
|
|
|
|
|
|
public class RuihongPostInvoiceModel
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 是 20 销货方纳税人识别号。
|
|
|
/// </summary>
|
|
|
public string taxpayerCode { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 100 销货方纳税人名称
|
|
|
/// </summary>
|
|
|
public string taxpayerName { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 79 销货方地址。如果填写则使用填写的信息,否则使用纳税人注册时预留的信息。
|
|
|
/// </summary>
|
|
|
public string taxpayerAddress { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 20 销货方电话。
|
|
|
/// </summary>
|
|
|
public string taxpayerTel { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 69 销货方开户银行。
|
|
|
/// </summary>
|
|
|
public string taxpayerBankName { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 30 销货方银行账号。
|
|
|
/// </summary>
|
|
|
public string taxpayerBankAccount { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 100 购货方名称,即发票抬头。
|
|
|
/// </summary>
|
|
|
public string customerName { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 20 购货方纳税人识别号或者个人身份证号
|
|
|
/// </summary>
|
|
|
public string customerCode { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 79 购货方地址。
|
|
|
/// </summary>
|
|
|
public string customerAddress { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 20 购货方电话。
|
|
|
/// </summary>
|
|
|
public string customerTel { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 69 购货方开户银行。
|
|
|
/// </summary>
|
|
|
public string customerBankName { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 30 购货方银行账号。
|
|
|
/// </summary>
|
|
|
public string customerBankAccount { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 2 开票类型。p:电子增值税普通发票(默认) ps:电子收购发票 py:成品油
|
|
|
/// </summary>
|
|
|
public string invoiceType { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 50 店铺编号
|
|
|
/// </summary>
|
|
|
public string shopCode { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 50 店铺名称
|
|
|
/// </summary>
|
|
|
public string shopName { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 50 支付方式
|
|
|
/// </summary>
|
|
|
public string payType { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 50 支付流水号
|
|
|
/// </summary>
|
|
|
public string payBillNo { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 8 开票人。
|
|
|
/// </summary>
|
|
|
public string drawer { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 8 收款人。
|
|
|
/// </summary>
|
|
|
public string payee { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 8 复核人。
|
|
|
/// </summary>
|
|
|
public string reviewer { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 18(整)2(小) 税价合计金额。必须大于等于0.01元;必须等于发票明细合计金额;必须小于等于在税务局进行票种核定时确定的单张发票开票限额。
|
|
|
/// </summary>
|
|
|
public string totalAmount { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 130 发票备注。
|
|
|
/// </summary>
|
|
|
public string remark { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 100 发票项目明细列表。每张发票最多一百条。
|
|
|
/// </summary>
|
|
|
public List<RuihongPostInvoiceItemModel> items { get; set; }
|
|
|
}
|
|
|
|
|
|
public class RuihongPostNoticeModel
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 是 通知类型。短信:sms;电子邮件:email。
|
|
|
/// </summary>
|
|
|
public string type { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 通知类型为短信时,必须为手机号码;通知类型为电子邮件时,必须为邮件地址。
|
|
|
/// </summary>
|
|
|
public string value { get; set; }
|
|
|
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 查票请求
|
|
|
public class RuihongQueryModel
|
|
|
{
|
|
|
public string serialNo { get; set; }
|
|
|
public string postTime { get; set; }
|
|
|
public List<RuihongQueryInner> criteria { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
public class RuihongQueryInner
|
|
|
{
|
|
|
public string name { get; set; }
|
|
|
public string value { get; set; }
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 冲红请求
|
|
|
public class RuihongRedInvoiceModel
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 是 50 操作流水号。传入重复的操作流水号则认为是重复操作。
|
|
|
/// </summary>
|
|
|
public string serialNo { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 19 请求发送时间。格式为yyyy-MM-dd HH:mm:ss。
|
|
|
/// </summary>
|
|
|
public string postTime { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 20 原发票代码+原发票号码。
|
|
|
/// </summary>
|
|
|
public string originalCode { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是 250 冲红原因。
|
|
|
/// </summary>
|
|
|
public string reason { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 3 通知方式列表。
|
|
|
/// </summary>
|
|
|
public List<RuihongPostNoticeModel> notices { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 否 100 发票项目明细列表。每张发票最多一百条。如果为空则按照原发票金额和明细全额冲红。如果不为空则按照该明细和金额进行部分冲红。
|
|
|
/// </summary>
|
|
|
public List<RuihongPostInvoiceItemModel> items { get; set; }
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 返回
|
|
|
public class RuihongRespCommon
|
|
|
{
|
|
|
public string serialNo { get; set; }
|
|
|
public string postTime { get; set; }
|
|
|
public string code { get; set; }
|
|
|
public string message { get; set; }
|
|
|
}
|
|
|
|
|
|
public class RuihongRespQuery : RuihongRespCommon
|
|
|
{
|
|
|
public List<RuihongRespQueryInner> invoices { get; set; }
|
|
|
}
|
|
|
|
|
|
public class RuihongRespQueryInner
|
|
|
{
|
|
|
public string orderNo { get; set; }
|
|
|
public string code { get; set; }
|
|
|
public string checkCode { get; set; }
|
|
|
public string fiscalCode { get; set; }
|
|
|
public string status { get; set; }
|
|
|
public string generateTime { get; set; }
|
|
|
public string pdfUnsignedUrl { get; set; }
|
|
|
public string viewUrl { get; set; }
|
|
|
public string relatedCode { get; set; }
|
|
|
public string validReason { get; set; }
|
|
|
public string validTime { get; set; }
|
|
|
}
|
|
|
#endregion
|
|
|
#endregion
|
|
|
}
|