| @@ -0,0 +1,6 @@ | |||
| .DS_Store | |||
| java/.idea/ | |||
| java/out/ | |||
| java/*.iml | |||
| cs/.vs/ | |||
| @@ -0,0 +1,22 @@ | |||
| | |||
| Microsoft Visual Studio Solution File, Format Version 12.00 | |||
| # Visual Studio 14 | |||
| VisualStudioVersion = 14.0.24720.0 | |||
| MinimumVisualStudioVersion = 10.0.40219.1 | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "einv-demo-order-cs", "einv-demo-order-cs\einv-demo-order-cs.csproj", "{EB51574C-DA86-4CBE-BC6B-1FCA249B0D6A}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| Debug|Any CPU = Debug|Any CPU | |||
| Release|Any CPU = Release|Any CPU | |||
| EndGlobalSection | |||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
| {EB51574C-DA86-4CBE-BC6B-1FCA249B0D6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {EB51574C-DA86-4CBE-BC6B-1FCA249B0D6A}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {EB51574C-DA86-4CBE-BC6B-1FCA249B0D6A}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {EB51574C-DA86-4CBE-BC6B-1FCA249B0D6A}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| EndGlobalSection | |||
| EndGlobal | |||
| @@ -0,0 +1,196 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using com.chinaeinv.einv.order; | |||
| using com.chinaeinv.einv.order.dto; | |||
| using System.Collections; | |||
| namespace einv_demo_order_cs | |||
| { | |||
| class Program | |||
| { | |||
| // 由电子发票平台分配的appCode | |||
| private static string appCode = "请向瑞宏网工作人员索取"; | |||
| // 证书文件路径 | |||
| private static string keyStorePath = "请向瑞宏网工作人员索取"; | |||
| // 证书密码 | |||
| private static string password = "请向瑞宏网工作人员索取"; | |||
| // 接口URL | |||
| private static string apiUrl = "https://www.chinaeinv.com:943/igs/api/orderApi.jspa"; | |||
| // 销售方纳税人识别号 | |||
| private static string testTaxpayerCode = "请向瑞宏网工作人员索取"; | |||
| private static OrderFacade orderFacade = new OrderFacadeAgent(appCode, keyStorePath, password, apiUrl); | |||
| static void Main(string[] args) | |||
| { | |||
| // (异步)开具蓝字发票 | |||
| TestKpAsync(); | |||
| // (异步)开具红字发票 | |||
| //TestChAsync(); | |||
| // (异步)对开票失败的订单重新开票 | |||
| //TestRetryKpAsync(); | |||
| // (同步)取消订单 | |||
| //TestCancel(); | |||
| // (同步)根据订单编号查询发票 | |||
| //TestCxByOrderNo(); | |||
| Console.WriteLine("按回车键退出。"); | |||
| Console.ReadLine(); //等待用户按一个回车 | |||
| return; //可选,按下回车后关闭 | |||
| } | |||
| /** | |||
| * (异步)开具蓝字发票 | |||
| */ | |||
| private static void TestKpAsync() | |||
| { | |||
| Order order = new Order(); | |||
| // 订单编号 | |||
| order.orderNo = "OrderNo-007"; | |||
| // 销货方纳税人识别号 | |||
| order.taxpayerCode = testTaxpayerCode; | |||
| // 用户扫码key | |||
| order.scanCodeKey = Guid.NewGuid().ToString(); | |||
| // 店铺 | |||
| order.shopName = "第198店"; | |||
| // 店铺编号 | |||
| order.shopCode = "198"; | |||
| // 订单时间(yyyy-MM-dd HH:mm:ss) | |||
| order.orderTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); | |||
| // 联系人 | |||
| order.contact = "小明"; | |||
| // 联系电话 | |||
| order.contactTel = "18000000000"; | |||
| // 邮箱 | |||
| order.contactMail = "test@chinaeinv.cn"; | |||
| // 配送地址 | |||
| order.shippingAddress = "火星北路1024号"; | |||
| // 购货方名称,即发票抬头 | |||
| order.customerName = "小明"; | |||
| // 是否自动开票 | |||
| order.autoBilling = true; | |||
| // 开票人 | |||
| order.drawer = "张三"; | |||
| // 收款人 | |||
| order.payee = "李四"; | |||
| // 复核人 | |||
| order.reviewer = "王五"; | |||
| // 开票金额 | |||
| order.totalAmount = "24000"; | |||
| // 发票备注 | |||
| order.remark = "软件系统维护期限为2018年1月1日至2019年12月31日。"; | |||
| // 发票明细1 | |||
| OrderItem orderItem1 = new OrderItem(); | |||
| //商品编码 | |||
| orderItem1.code = "001"; | |||
| //商品名称 | |||
| orderItem1.name = "软件开发"; | |||
| //规格型号 | |||
| orderItem1.spec = "无"; | |||
| //税率 | |||
| orderItem1.taxRate = "0.06"; | |||
| //税价合计金额 | |||
| orderItem1.amount = "10000"; | |||
| //商品分类编码 | |||
| orderItem1.catalogCode = "3040203000000000000"; | |||
| // 发票明细2 | |||
| OrderItem orderItem2 = new OrderItem(); | |||
| //商品编码 | |||
| orderItem2.code = "002"; | |||
| //商品名称 | |||
| orderItem2.name = "软件系统维护"; | |||
| //规格型号 | |||
| orderItem2.spec = "无"; | |||
| //商品单价 | |||
| orderItem2.price = "8000"; | |||
| //数量 | |||
| orderItem2.quantity = "2"; | |||
| //单位 | |||
| orderItem2.uom = "年"; | |||
| //税率 | |||
| orderItem2.taxRate = "0.06"; | |||
| //税价合计金额 | |||
| orderItem2.amount = "16000"; | |||
| //折扣金额 | |||
| orderItem2.discountAmount = "2000"; | |||
| //商品分类编码 | |||
| orderItem2.catalogCode = "3040203000000000000"; | |||
| // 发票明细 | |||
| order.orderItems = new List<OrderItem>(); | |||
| order.orderItems.Add(orderItem1); | |||
| order.orderItems.Add(orderItem2); | |||
| // 扩展参数 | |||
| order.extendedParams = new Hashtable(); | |||
| order.extendedParams.Add("testExtendedNo1", "12345678"); | |||
| order.extendedParams.Add("testExtendedNo2", "87654321"); | |||
| // 调用接口方法 | |||
| orderFacade.KpAsync(order); | |||
| } | |||
| /** | |||
| * (异步)开具红字发票 | |||
| */ | |||
| private static void TestChAsync() | |||
| { | |||
| Order order = new Order(); | |||
| // 订单编号 | |||
| order.orderNo = "OrderNo-006"; | |||
| // 销货方纳税人识别号 | |||
| order.taxpayerCode = testTaxpayerCode; | |||
| // 冲红原因 | |||
| order.reason = "开票信息有误。"; | |||
| // 调用接口方法 | |||
| orderFacade.ChAsync(order); | |||
| } | |||
| /** | |||
| * (异步)对开票失败的订单重新开票 | |||
| */ | |||
| private static void TestRetryKpAsync() | |||
| { | |||
| OrderBase order = new OrderBase(); | |||
| // 订单编号 | |||
| order.orderNo = "OrderNo-004"; | |||
| // 销货方纳税人识别号 | |||
| order.taxpayerCode = testTaxpayerCode; | |||
| // 调用接口方法 | |||
| orderFacade.RetryKpAsync(order); | |||
| } | |||
| /** | |||
| * (同步)取消订单 | |||
| */ | |||
| private static void TestCancel() | |||
| { | |||
| OrderBase order = new OrderBase(); | |||
| // 订单编号 | |||
| order.orderNo = "OrderNo-004"; | |||
| // 销货方纳税人识别号 | |||
| order.taxpayerCode = testTaxpayerCode; | |||
| // 调用接口方法 | |||
| orderFacade.Cancel(order); | |||
| } | |||
| /** | |||
| * (同步)根据订单编号查询发票 | |||
| */ | |||
| private static void TestCxByOrderNo() | |||
| { | |||
| OrderBase orderBase = new OrderBase("OrderNo-006", testTaxpayerCode); | |||
| // 调用接口方法 | |||
| orderFacade.CxByOrderNo(orderBase); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| using System.Reflection; | |||
| using System.Runtime.CompilerServices; | |||
| using System.Runtime.InteropServices; | |||
| // 有关程序集的一般信息由以下 | |||
| // 控制。更改这些特性值可修改 | |||
| // 与程序集关联的信息。 | |||
| [assembly: AssemblyTitle("einv-demo-order-cs")] | |||
| [assembly: AssemblyDescription("")] | |||
| [assembly: AssemblyConfiguration("")] | |||
| [assembly: AssemblyCompany("")] | |||
| [assembly: AssemblyProduct("einv-demo-order-cs")] | |||
| [assembly: AssemblyCopyright("Copyright © 2018")] | |||
| [assembly: AssemblyTrademark("")] | |||
| [assembly: AssemblyCulture("")] | |||
| //将 ComVisible 设置为 false 将使此程序集中的类型 | |||
| //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, | |||
| //请将此类型的 ComVisible 特性设置为 true。 | |||
| [assembly: ComVisible(false)] | |||
| // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID | |||
| [assembly: Guid("eb51574c-da86-4cbe-bc6b-1fca249b0d6a")] | |||
| // 程序集的版本信息由下列四个值组成: | |||
| // | |||
| // 主版本 | |||
| // 次版本 | |||
| // 生成号 | |||
| // 修订号 | |||
| // | |||
| //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, | |||
| // 方法是按如下所示使用“*”: : | |||
| // [assembly: AssemblyVersion("1.0.*")] | |||
| [assembly: AssemblyVersion("1.0.0.0")] | |||
| [assembly: AssemblyFileVersion("1.0.0.0")] | |||
| @@ -0,0 +1,34 @@ | |||
| using com.chinaeinv.einv.order.dto; | |||
| namespace com.chinaeinv.einv.order | |||
| { | |||
| /** | |||
| * 订单接口 | |||
| */ | |||
| public interface OrderFacade { | |||
| /** | |||
| * (异步)开具蓝字发票异步方法 | |||
| */ | |||
| Result KpAsync(Order order); | |||
| /** | |||
| * (异步)开具红字发票异步方法 | |||
| */ | |||
| Result ChAsync(Order order); | |||
| /** | |||
| * (异步)对开票失败的订单重新开票异步方法 | |||
| */ | |||
| Result RetryKpAsync(OrderBase orderBase); | |||
| /** | |||
| * (同步)取消订单 | |||
| */ | |||
| Result Cancel(OrderBase orderBase); | |||
| /** | |||
| * (同步)根据订单编号查询发票 | |||
| */ | |||
| InvoiceResult CxByOrderNo(OrderBase orderBase); | |||
| } | |||
| } | |||
| @@ -0,0 +1,133 @@ | |||
| using System; | |||
| using System.Net; | |||
| using System.Text; | |||
| using System.Web; | |||
| using System.Security.Cryptography; | |||
| using System.Security.Cryptography.X509Certificates; | |||
| using com.chinaeinv.einv.order.dto; | |||
| using Newtonsoft.Json; | |||
| namespace com.chinaeinv.einv.order | |||
| { | |||
| /** | |||
| * 订单接口代理实现 | |||
| */ | |||
| public class OrderFacadeAgent : OrderFacade { | |||
| private string appCode, keyStorePath, password, apiUrl; | |||
| /** | |||
| * 构造方法 | |||
| * @param appCode 由电子发票平台分配的appCode | |||
| * @param keyStorePath 密钥库文件路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @param apiUrl 接口URL | |||
| */ | |||
| public OrderFacadeAgent(string appCode, string keyStorePath, string password, string apiUrl) { | |||
| this.appCode = appCode; | |||
| this.keyStorePath = keyStorePath; | |||
| this.password = password; | |||
| this.apiUrl = apiUrl; | |||
| } | |||
| /** | |||
| * (异步)开具蓝字发票 | |||
| */ | |||
| public Result KpAsync(Order order) { | |||
| return Exec<Result, Order>(order, "chinaeinv.api.order.v11.kp_async"); | |||
| } | |||
| /** | |||
| * (异步)开具红字发票 | |||
| */ | |||
| public Result ChAsync(Order order) { | |||
| return Exec<Result, Order>(order, "chinaeinv.api.order.v11.ch_async"); | |||
| } | |||
| /** | |||
| * (异步)对开票失败的订单重新开票 | |||
| */ | |||
| public Result RetryKpAsync(OrderBase orderBase) { | |||
| return Exec<Result, OrderBase>(orderBase, "chinaeinv.api.order.v11.retryKp_async"); | |||
| } | |||
| /** | |||
| * (同步)取消订单 | |||
| */ | |||
| public Result Cancel(OrderBase orderBase) { | |||
| return Exec<Result, OrderBase>(orderBase, "chinaeinv.api.order.v11.cancel"); | |||
| } | |||
| /** | |||
| * (同步)根据订单编号查询发票 | |||
| */ | |||
| public InvoiceResult CxByOrderNo(OrderBase orderBase) { | |||
| return Exec<InvoiceResult, OrderBase>(orderBase, "chinaeinv.api.order.v11.cx.orderNo"); | |||
| } | |||
| /** | |||
| * 调用接口 | |||
| * @param order 请求数据 | |||
| * @return 响应数据 | |||
| * @throws Exception | |||
| */ | |||
| private T Exec<T,O>(O order, string cmdName) { | |||
| // 将Order对象转为json | |||
| string requestJson = JsonConvert.SerializeObject(order, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); | |||
| Console.WriteLine("方法名称: " + cmdName + ", 请求报文: " + requestJson); | |||
| // 生成签名 | |||
| string sign = GetSign(requestJson); | |||
| Console.WriteLine("方法名称: " + cmdName + ", 签名: " + sign); | |||
| // 调用接口 | |||
| string responseJson; | |||
| using (var client = new WebClient()) | |||
| { | |||
| client.Encoding = Encoding.UTF8; | |||
| client.QueryString.Add("appCode", appCode); | |||
| client.QueryString.Add("cmdName", cmdName); | |||
| client.QueryString.Add("sign", UrlEncode(UrlEncode(sign, Encoding.UTF8), Encoding.UTF8)); // C#中需要进行两次UrlEncode,不然发送请求时加号会变空格,导致验签失败。 | |||
| responseJson = client.UploadString(apiUrl, requestJson); | |||
| } | |||
| Console.WriteLine("方法名称: " + cmdName + ", 响应报文: " + responseJson); | |||
| // 将响应json转为对象 | |||
| return JsonConvert.DeserializeObject<T>(responseJson); | |||
| } | |||
| /** | |||
| * 数字签名 | |||
| */ | |||
| private string GetSign(string kpParams) | |||
| { | |||
| X509Certificate2 x = new X509Certificate2(keyStorePath, password, 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"); | |||
| return Convert.ToBase64String(sign); | |||
| } | |||
| 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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,183 @@ | |||
| using System.Collections.Generic; | |||
| namespace com.chinaeinv.einv.order.dto | |||
| { | |||
| /** | |||
| * 开票请求中的发票信息 | |||
| */ | |||
| public class Invoice | |||
| { | |||
| /** | |||
| * 订单编号 | |||
| */ | |||
| public string orderNo { get; set; } | |||
| /** | |||
| * 子订单编号 | |||
| */ | |||
| public string subOrderNo { get; set; } | |||
| /** | |||
| * 店铺名称 | |||
| */ | |||
| public string shopName { get; set; } | |||
| /** | |||
| * 店铺编号 | |||
| */ | |||
| public string shopCode { get; set; } | |||
| /** | |||
| * 销货方纳税人识别号 | |||
| */ | |||
| public string taxpayerCode { get; set; } | |||
| /** | |||
| * 销货方名称 | |||
| */ | |||
| public string taxpayerName { get; set; } | |||
| /** | |||
| * 销货方地址 | |||
| */ | |||
| public string taxpayerAddress { get; set; } | |||
| /** | |||
| * 销货方电话 | |||
| */ | |||
| public string taxpayerTel { get; set; } | |||
| /** | |||
| * 销货方开户银行 | |||
| */ | |||
| public string taxpayerBankName { get; set; } | |||
| /** | |||
| * 销货方银行账号 | |||
| */ | |||
| public string taxpayerBankAccount { get; set; } | |||
| /** | |||
| * 购货方纳税人识别号 | |||
| */ | |||
| public string customerCode { get; set; } | |||
| /** | |||
| * 购货方名称,即发票抬头 | |||
| */ | |||
| public string customerName { get; set; } | |||
| /** | |||
| * 购货方地址 | |||
| */ | |||
| public string customerAddress { get; set; } | |||
| /** | |||
| * 购货方电话 | |||
| */ | |||
| public string customerTel { get; set; } | |||
| /** | |||
| * 购货方开户银行 | |||
| */ | |||
| public string customerBankName { get; set; } | |||
| /** | |||
| * 购货方银行账号 | |||
| */ | |||
| public string customerBankAccount { get; set; } | |||
| /** | |||
| * 发票代码+发票号码 | |||
| */ | |||
| public string code { get; set; } | |||
| /** | |||
| * 校验码 | |||
| */ | |||
| public string checkCode { get; set; } | |||
| /** | |||
| * 税控码 | |||
| */ | |||
| public string fiscalCode { get; set; } | |||
| /** | |||
| * 发票状态 | |||
| * 正常:1 | |||
| * 红冲:3 | |||
| * 被红冲:4 | |||
| */ | |||
| public string status { get; set; } | |||
| /** | |||
| * 生成时间 | |||
| */ | |||
| public string generateTime { get; set; } | |||
| /** | |||
| * 税价合计金额 | |||
| */ | |||
| public string totalAmount { get; set; } | |||
| /** | |||
| * 不含税金额 | |||
| */ | |||
| public string noTaxAmount { get; set; } | |||
| /** | |||
| * 税额 | |||
| */ | |||
| public string taxAmount { get; set; } | |||
| /** | |||
| * 开票人 | |||
| */ | |||
| public string drawer { get; set; } | |||
| /** | |||
| * 收款人 | |||
| */ | |||
| public string payee { get; set; } | |||
| /** | |||
| * 复核人 | |||
| */ | |||
| public string reviewer { get; set; } | |||
| /** | |||
| * 发票备注 | |||
| */ | |||
| public string remark { get; set; } | |||
| /** | |||
| * 未签名PDF下载URL | |||
| */ | |||
| public string pdfUnsignedUrl { get; set; } | |||
| /** | |||
| * 查看URL | |||
| */ | |||
| public string viewUrl { get; set; } | |||
| /** | |||
| * 关联发票编号 | |||
| */ | |||
| public string relatedCode { get; set; } | |||
| /** | |||
| * 作废/红冲原因 | |||
| */ | |||
| public string validReason { get; set; } | |||
| /** | |||
| * 作废/红冲时间 | |||
| */ | |||
| public string validTime { get; set; } | |||
| /** | |||
| * 项目明细 | |||
| */ | |||
| public List<InvoiceItem> items { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,82 @@ | |||
| namespace com.chinaeinv.einv.order.dto | |||
| { | |||
| /** | |||
| * 发票项目明细 | |||
| */ | |||
| public class InvoiceItem { | |||
| /** | |||
| * 发票行性质 0 正常行、1 折扣行、2 被折扣行 | |||
| */ | |||
| public string type { get; set; } | |||
| /** | |||
| * 商品编码 | |||
| */ | |||
| public string code { get; set; } | |||
| /** | |||
| * 商品名称 | |||
| */ | |||
| public string name { get; set; } | |||
| /** | |||
| * 规格型号 | |||
| */ | |||
| public string spec { get; set; } | |||
| /** | |||
| * 含税商品单价 | |||
| */ | |||
| public string price { get; set; } | |||
| /** | |||
| * 数量 | |||
| */ | |||
| public string quantity { get; set; } | |||
| /** | |||
| * 单位 | |||
| */ | |||
| public string uom { get; set; } | |||
| /** | |||
| * 税率 | |||
| */ | |||
| public string taxRate { get; set; } | |||
| /** | |||
| * 税价合计金额 | |||
| */ | |||
| public string amount { get; set; } | |||
| /** | |||
| * 不含税金额 | |||
| */ | |||
| public string noTaxAmount { get; set; } | |||
| /** | |||
| * 税额 | |||
| */ | |||
| public string taxAmount { get; set; } | |||
| /** | |||
| * 商品分类编码 | |||
| */ | |||
| public string catalogCode { get; set; } | |||
| /** | |||
| * 优惠政策标识 | |||
| */ | |||
| public string preferentialPolicyFlg { get; set; } | |||
| /** | |||
| * 增值税特殊管理 | |||
| */ | |||
| public string addedValueTaxFlg { get; set; } | |||
| /** | |||
| * 零税率标识 | |||
| */ | |||
| public string zeroTaxRateFlg { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| using System.Collections.Generic; | |||
| namespace com.chinaeinv.einv.order.dto | |||
| { | |||
| /** | |||
| * “根据订单编号查询发票”方法响应和回调数据 | |||
| */ | |||
| public class InvoiceResult : Result { | |||
| /** | |||
| * 发票列表 | |||
| */ | |||
| public List<Invoice> invoices { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,156 @@ | |||
| using System.Collections.Generic; | |||
| using System.Collections; | |||
| using System; | |||
| namespace com.chinaeinv.einv.order.dto | |||
| { | |||
| /** | |||
| * 订单详细信息 | |||
| */ | |||
| public class Order : OrderBase { | |||
| /** | |||
| * 用户扫码key | |||
| */ | |||
| public string scanCodeKey { get; set; } | |||
| /** | |||
| * 店铺 | |||
| */ | |||
| public string shopName { get; set; } | |||
| /** | |||
| * 店铺编号 | |||
| */ | |||
| public string shopCode { get; set; } | |||
| /** | |||
| * 订单时间(yyyy-MM-dd HH:mm:ss) | |||
| */ | |||
| public string orderTime { get; set; } | |||
| /** | |||
| * 联系人 | |||
| */ | |||
| public string contact { get; set; } | |||
| /** | |||
| * 联系电话 | |||
| */ | |||
| public string contactTel { get; set; } | |||
| /** | |||
| * 邮箱 | |||
| */ | |||
| public string contactMail { get; set; } | |||
| /** | |||
| * 配送地址 | |||
| */ | |||
| public string shippingAddress { get; set; } | |||
| /** | |||
| * 销货方名称 | |||
| */ | |||
| public string taxpayerName { get; set; } | |||
| /** | |||
| * 销货方地址 | |||
| */ | |||
| public string taxpayerAddress { get; set; } | |||
| /** | |||
| * 销货方电话 | |||
| */ | |||
| public string taxpayerTel { get; set; } | |||
| /** | |||
| * 销货方开户银行 | |||
| */ | |||
| public string taxpayerBankName { get; set; } | |||
| /** | |||
| * 销货方银行账号 | |||
| */ | |||
| public string taxpayerBankAccount { get; set; } | |||
| /** | |||
| * 购货方名称,即发票抬头 | |||
| */ | |||
| public string customerName { get; set; } | |||
| /** | |||
| * 购货方纳税人识别号 | |||
| */ | |||
| public string customerCode { get; set; } | |||
| /** | |||
| * 购货方地址 | |||
| */ | |||
| public string customerAddress { get; set; } | |||
| /** | |||
| * 购货方电话 | |||
| */ | |||
| public string customerTel { get; set; } | |||
| /** | |||
| * 购货方开户银行 | |||
| */ | |||
| public string customerBankName { get; set; } | |||
| /** | |||
| * 购货方银行账号 | |||
| */ | |||
| public string customerBankAccount { get; set; } | |||
| /** | |||
| * 是否自动开票 | |||
| */ | |||
| public Boolean autoBilling { get; set; } | |||
| /** | |||
| * 开票人 | |||
| */ | |||
| public string drawer { get; set; } | |||
| /** | |||
| * 收款人 | |||
| */ | |||
| public string payee { get; set; } | |||
| /** | |||
| * 复核人 | |||
| */ | |||
| public string reviewer { get; set; } | |||
| /** | |||
| * 开票金额 | |||
| */ | |||
| public string totalAmount { get; set; } | |||
| /** | |||
| * 发票备注 | |||
| */ | |||
| public string remark { get; set; } | |||
| /** | |||
| * 冲红原因 | |||
| */ | |||
| public string reason { get; set; } | |||
| /** | |||
| * 订单明细 | |||
| */ | |||
| public List<OrderItem> orderItems { get; set; } | |||
| /** | |||
| * 扩展参数 | |||
| */ | |||
| public Hashtable extendedParams { get; set; } | |||
| /** | |||
| * 动态参数 | |||
| */ | |||
| public Hashtable dynamicParams { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| namespace com.chinaeinv.einv.order.dto | |||
| { | |||
| /** | |||
| * 订单基本信息 | |||
| */ | |||
| public class OrderBase { | |||
| /** | |||
| * 订单编号 | |||
| */ | |||
| public string orderNo { get; set; } | |||
| /** | |||
| * 子订单编号 | |||
| */ | |||
| public string subOrderNo { get; set; } | |||
| /** | |||
| * 销货方纳税人识别号 | |||
| */ | |||
| public string taxpayerCode { get; set; } | |||
| /** | |||
| * 发票类型 | |||
| */ | |||
| public string invoiceType { get; set; } | |||
| public OrderBase() { | |||
| } | |||
| public OrderBase(string orderNo, string taxpayerCode) { | |||
| this.orderNo = orderNo; | |||
| this.taxpayerCode = taxpayerCode; | |||
| } | |||
| public OrderBase(string orderNo, string subOrderNo, string taxpayerCode) { | |||
| this.orderNo = orderNo; | |||
| this.subOrderNo = subOrderNo; | |||
| this.taxpayerCode = taxpayerCode; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,69 @@ | |||
| namespace com.chinaeinv.einv.order.dto | |||
| { | |||
| public class OrderItem { | |||
| /** | |||
| * 商品编码 | |||
| */ | |||
| public string code { get; set; } | |||
| /** | |||
| * 商品名称 | |||
| */ | |||
| public string name { get; set; } | |||
| /** | |||
| * 规格型号 | |||
| */ | |||
| public string spec { get; set; } | |||
| /** | |||
| * 商品单价 | |||
| */ | |||
| public string price { get; set; } | |||
| /** | |||
| * 数量 | |||
| */ | |||
| public string quantity { get; set; } | |||
| /** | |||
| * 单位 | |||
| */ | |||
| public string uom { get; set; } | |||
| /** | |||
| * 税率 | |||
| */ | |||
| public string taxRate { get; set; } | |||
| /** | |||
| * 税价合计金额 | |||
| */ | |||
| public string amount { get; set; } | |||
| /** | |||
| * 折扣金额 | |||
| */ | |||
| public string discountAmount { get; set; } | |||
| /** | |||
| * 商品分类编码 | |||
| */ | |||
| public string catalogCode { get; set; } | |||
| /** | |||
| * 优惠政策标识 | |||
| */ | |||
| public string preferentialPolicyFlg { get; set; } | |||
| /** | |||
| * 增值税特殊管理 | |||
| */ | |||
| public string addedValueTaxFlg { get; set; } | |||
| /** | |||
| * 零税率标识 | |||
| */ | |||
| public string zeroTaxRateFlg { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| namespace com.chinaeinv.einv.order.dto | |||
| { | |||
| /** | |||
| * 异步方法响应和“取消订单”方法响应 | |||
| */ | |||
| public class Result { | |||
| /** | |||
| * 处理结果代码 | |||
| */ | |||
| public string code { get; set; } | |||
| /** | |||
| * 处理结果消息 | |||
| */ | |||
| public string message { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,85 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
| <PropertyGroup> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
| <ProjectGuid>{EB51574C-DA86-4CBE-BC6B-1FCA249B0D6A}</ProjectGuid> | |||
| <OutputType>Exe</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>einv_demo_order_cs</RootNamespace> | |||
| <AssemblyName>einv-demo-order-cs</AssemblyName> | |||
| <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | |||
| <FileAlignment>512</FileAlignment> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
| <PlatformTarget>AnyCPU</PlatformTarget> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <DebugType>full</DebugType> | |||
| <Optimize>false</Optimize> | |||
| <OutputPath>bin\Debug\</OutputPath> | |||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
| <PlatformTarget>AnyCPU</PlatformTarget> | |||
| <DebugType>pdbonly</DebugType> | |||
| <Optimize>true</Optimize> | |||
| <OutputPath>bin\Release\</OutputPath> | |||
| <DefineConstants>TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Reference Include="Newtonsoft.Json, Version=4.0.3.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | |||
| <SpecificVersion>False</SpecificVersion> | |||
| <HintPath>lib\Newtonsoft.Json.dll</HintPath> | |||
| </Reference> | |||
| <Reference Include="System" /> | |||
| <Reference Include="System.Core" /> | |||
| <Reference Include="System.Runtime.Serialization" /> | |||
| <Reference Include="System.Security" /> | |||
| <Reference Include="System.Web" /> | |||
| <Reference Include="System.Xml.Linq" /> | |||
| <Reference Include="System.Data.DataSetExtensions" /> | |||
| <Reference Include="Microsoft.CSharp" /> | |||
| <Reference Include="System.Data" /> | |||
| <Reference Include="System.Xml" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="com\chinaeinv\einv\order\dto\Invoice.cs" /> | |||
| <Compile Include="com\chinaeinv\einv\order\dto\InvoiceItem.cs" /> | |||
| <Compile Include="com\chinaeinv\einv\order\dto\InvoiceResult.cs" /> | |||
| <Compile Include="com\chinaeinv\einv\order\dto\Order.cs" /> | |||
| <Compile Include="com\chinaeinv\einv\order\dto\OrderBase.cs" /> | |||
| <Compile Include="com\chinaeinv\einv\order\dto\OrderItem.cs" /> | |||
| <Compile Include="com\chinaeinv\einv\order\dto\Result.cs" /> | |||
| <Compile Include="com\chinaeinv\einv\order\OrderFacade.cs" /> | |||
| <Compile Include="com\chinaeinv\einv\order\OrderFacadeAgent.cs" /> | |||
| <Compile Include="Program.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Content Include="lib\Newtonsoft.Json.dll"> | |||
| <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
| </Content> | |||
| <Content Include="lib\Newtonsoft.Json.pdb"> | |||
| <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
| </Content> | |||
| <Content Include="lib\Newtonsoft.Json.xml"> | |||
| <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
| </Content> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Include="PTTEST17.pfx" /> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
| Other similar extension points exist, see Microsoft.Common.targets. | |||
| <Target Name="BeforeBuild"> | |||
| </Target> | |||
| <Target Name="AfterBuild"> | |||
| </Target> | |||
| --> | |||
| </Project> | |||
| @@ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <ProjectView>ProjectFiles</ProjectView> | |||
| </PropertyGroup> | |||
| </Project> | |||
| @@ -0,0 +1,191 @@ | |||
| import com.chinaeinv.einv.order.OrderFacade; | |||
| import com.chinaeinv.einv.order.OrderFacadeAgent; | |||
| import com.chinaeinv.einv.order.dto.*; | |||
| import java.math.BigDecimal; | |||
| import java.time.LocalDateTime; | |||
| import java.time.format.DateTimeFormatter; | |||
| import java.util.*; | |||
| public class Main { | |||
| // 由电子发票平台分配的appCode | |||
| private static final String appCode = "请向瑞宏网工作人员索取"; | |||
| // 密钥库文件路径 | |||
| private static final String keyStorePath = "请向瑞宏网工作人员索取"; | |||
| // 密钥库别名 | |||
| private static final String alias = "请向瑞宏网工作人员索取"; | |||
| // 密钥库密码 | |||
| private static final String password = "请向瑞宏网工作人员索取"; | |||
| // 接口URL | |||
| private static final String apiUrl = "https://www.chinaeinv.com:943/igs/api/orderApi.jspa"; | |||
| // 销售方纳税人识别号 | |||
| private static final String testTaxpayerCode = "请向瑞宏网工作人员索取"; | |||
| private static final DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); | |||
| private static OrderFacade orderFacade = new OrderFacadeAgent(appCode, keyStorePath, alias, password, apiUrl); | |||
| private static String orderNo = UUID.randomUUID().toString(); | |||
| public static void main(String[] args) throws Exception { | |||
| System.out.println("订单编号:" + orderNo); | |||
| // (异步)开具蓝字发票 | |||
| testKpAsync(); | |||
| // (异步)开具红字发票 | |||
| //testChAsync(); | |||
| // (异步)对开票失败的订单重新开票 | |||
| //testRetryKpAsync(); | |||
| // (同步)取消订单 | |||
| //testCancel(); | |||
| // (同步)根据订单编号查询发票 | |||
| testCxByOrderNo(); | |||
| } | |||
| /** | |||
| * (异步)开具蓝字发票 | |||
| */ | |||
| private static void testKpAsync() throws Exception { | |||
| Order order = new Order(); | |||
| // 订单编号 | |||
| order.setOrderNo(orderNo); | |||
| // 销货方纳税人识别号 | |||
| order.setTaxpayerCode(testTaxpayerCode); | |||
| // 用户扫码key | |||
| order.setScanCodeKey(UUID.randomUUID().toString()); | |||
| // 店铺 | |||
| order.setShopName("第198店"); | |||
| // 店铺编号 | |||
| order.setShopCode("198"); | |||
| // 订单时间(yyyy-MM-dd HH:mm:ss) | |||
| order.setOrderTime(LocalDateTime.now().format(df)); | |||
| // 联系人 | |||
| order.setContact("小小明222"); | |||
| // 联系电话 | |||
| order.setContactTel("18000000000"); | |||
| // 邮箱 | |||
| order.setContactMail("test@chinaeinv.cn"); | |||
| // 配送地址 | |||
| order.setShippingAddress("火星北路1024号"); | |||
| // 购货方名称,即发票抬头 | |||
| order.setCustomerName(UUID.randomUUID().toString().replace("-","")); | |||
| order.setCustomerCode(""); | |||
| // 是否自动开票 | |||
| order.setAutoBilling(true); | |||
| // 开票人 | |||
| order.setDrawer("张三"); | |||
| // 收款人 | |||
| order.setPayee("李四"); | |||
| // 复核人 | |||
| order.setReviewer("王五"); | |||
| // 开票金额 | |||
| order.setTotalAmount(new BigDecimal("24000")); | |||
| // 发票备注 | |||
| order.setRemark("软件系统维护期限为2018年1月1日至2019年12月31日。"); | |||
| // 发票明细1 | |||
| OrderItem orderItem1 = new OrderItem(); | |||
| //商品编码 | |||
| orderItem1.setCode("001"); | |||
| //商品名称 | |||
| orderItem1.setName("软件开发"); | |||
| //规格型号 | |||
| orderItem1.setSpec("无"); | |||
| //税率 | |||
| orderItem1.setTaxRate(new BigDecimal("0.06")); | |||
| //税价合计金额 | |||
| orderItem1.setAmount(new BigDecimal("10000")); | |||
| //商品分类编码 | |||
| orderItem1.setCatalogCode("3040203000000000000"); | |||
| // 发票明细2 | |||
| OrderItem orderItem2 = new OrderItem(); | |||
| //商品编码 | |||
| orderItem2.setCode("002"); | |||
| //商品名称 | |||
| orderItem2.setName("软件系统维护"); | |||
| //规格型号 | |||
| orderItem2.setSpec("无"); | |||
| //商品单价 | |||
| orderItem2.setPrice(new BigDecimal("8000")); | |||
| //数量 | |||
| orderItem2.setQuantity(new BigDecimal("2")); | |||
| //单位 | |||
| orderItem2.setUom("年"); | |||
| //税率 | |||
| orderItem2.setTaxRate(new BigDecimal("0.06")); | |||
| //税价合计金额 | |||
| orderItem2.setAmount(new BigDecimal("16000")); | |||
| //折扣金额 | |||
| orderItem2.setDiscountAmount(new BigDecimal("2000")); | |||
| //商品分类编码 | |||
| orderItem2.setCatalogCode("3040203000000000000"); | |||
| // 发票明细 | |||
| order.setOrderItems(Arrays.asList(orderItem1, orderItem2)); | |||
| // 扩展参数 | |||
| order.setExtendedParams(Collections.singletonMap("testExtendedNo", "12345678")); | |||
| // 调用接口方法 | |||
| orderFacade.kpAsync(order); | |||
| } | |||
| /** | |||
| * (异步)开具红字发票 | |||
| */ | |||
| private static void testChAsync() throws Exception { | |||
| Order order = new Order(); | |||
| // 订单编号 | |||
| order.setOrderNo("OrderNo-105"); | |||
| // 销货方纳税人识别号 | |||
| order.setTaxpayerCode(testTaxpayerCode); | |||
| // 冲红原因 | |||
| order.setReason("开票信息有误。"); | |||
| // 调用接口方法 | |||
| orderFacade.chAsync(order); | |||
| } | |||
| /** | |||
| * (异步)对开票失败的订单重新开票 | |||
| */ | |||
| private static void testRetryKpAsync() throws Exception { | |||
| Order order = new Order(); | |||
| // 订单编号 | |||
| order.setOrderNo("OrderNo-001"); | |||
| // 销货方纳税人识别号 | |||
| order.setTaxpayerCode(testTaxpayerCode); | |||
| // 调用接口方法 | |||
| orderFacade.retryKpAsync(order); | |||
| } | |||
| /** | |||
| * (同步)取消订单 | |||
| */ | |||
| private static void testCancel() throws Exception { | |||
| Order order = new Order(); | |||
| // 订单编号 | |||
| order.setOrderNo("OrderNo-001"); | |||
| // 销货方纳税人识别号 | |||
| order.setTaxpayerCode(testTaxpayerCode); | |||
| // 调用接口方法 | |||
| orderFacade.cancel(order); | |||
| } | |||
| /** | |||
| * (同步)根据订单编号查询发票 | |||
| */ | |||
| private static void testCxByOrderNo() throws Exception { | |||
| OrderBase orderBase = new OrderBase(orderNo, testTaxpayerCode); | |||
| // 调用接口方法 | |||
| orderFacade.cxByOrderNo(orderBase); | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| package com.chinaeinv.einv.order; | |||
| import com.chinaeinv.einv.order.dto.InvoiceResult; | |||
| import com.chinaeinv.einv.order.dto.Order; | |||
| import com.chinaeinv.einv.order.dto.OrderBase; | |||
| import com.chinaeinv.einv.order.dto.Result; | |||
| /** | |||
| * 订单接口 | |||
| */ | |||
| public interface OrderFacade { | |||
| /** | |||
| * (异步)开具蓝字发票异步方法 | |||
| */ | |||
| Result kpAsync(Order order) throws Exception; | |||
| /** | |||
| * (异步)开具红字发票异步方法 | |||
| */ | |||
| Result chAsync(Order order) throws Exception; | |||
| /** | |||
| * (异步)对开票失败的订单重新开票异步方法 | |||
| */ | |||
| Result retryKpAsync(OrderBase orderBase) throws Exception; | |||
| /** | |||
| * (同步)取消订单 | |||
| */ | |||
| Result cancel(OrderBase orderBase) throws Exception; | |||
| /** | |||
| * (同步)根据订单编号查询发票 | |||
| */ | |||
| InvoiceResult cxByOrderNo(OrderBase orderBase) throws Exception; | |||
| } | |||
| @@ -0,0 +1,92 @@ | |||
| package com.chinaeinv.einv.order; | |||
| import com.alibaba.fastjson.JSON; | |||
| import com.chinaeinv.einv.order.dto.*; | |||
| import com.chinaeinv.einv.util.CertificateUtils; | |||
| import com.chinaeinv.einv.util.HttpUtil; | |||
| import java.net.URLEncoder; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * 订单接口代理实现 | |||
| */ | |||
| public class OrderFacadeAgent implements OrderFacade { | |||
| private String appCode, keyStorePath, alias, password, apiUrl; | |||
| /** | |||
| * 构造方法 | |||
| * @param appCode 由电子发票平台分配的appCode | |||
| * @param keyStorePath 密钥库文件路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @param apiUrl 接口URL | |||
| */ | |||
| public OrderFacadeAgent(String appCode, String keyStorePath, String alias, String password, String apiUrl) { | |||
| this.appCode = appCode; | |||
| this.keyStorePath = keyStorePath; | |||
| this.alias = alias; | |||
| this.password = password; | |||
| this.apiUrl = apiUrl; | |||
| } | |||
| /** | |||
| * (异步)开具蓝字发票 | |||
| */ | |||
| public Result kpAsync(Order order) throws Exception { | |||
| return exec(order, "chinaeinv.api.order.v11.kp_async", Result.class); | |||
| } | |||
| /** | |||
| * (异步)开具红字发票 | |||
| */ | |||
| public Result chAsync(Order order) throws Exception { | |||
| return exec(order, "chinaeinv.api.order.v11.ch_async", Result.class); | |||
| } | |||
| /** | |||
| * (异步)对开票失败的订单重新开票 | |||
| */ | |||
| public Result retryKpAsync(OrderBase orderBase) throws Exception { | |||
| return exec(orderBase, "chinaeinv.api.order.v11.retryKp_async", Result.class); | |||
| } | |||
| /** | |||
| * (同步)取消订单 | |||
| */ | |||
| public Result cancel(OrderBase orderBase) throws Exception { | |||
| return exec(orderBase, "chinaeinv.api.order.v11.cancel", Result.class); | |||
| } | |||
| /** | |||
| * (同步)根据订单编号查询发票 | |||
| */ | |||
| public InvoiceResult cxByOrderNo(OrderBase orderBase) throws Exception { | |||
| return exec(orderBase, "chinaeinv.api.order.v11.cx.orderNo", InvoiceResult.class); | |||
| } | |||
| /** | |||
| * 调用接口 | |||
| * @param order 请求数据 | |||
| * @return 响应数据 | |||
| * @throws Exception | |||
| */ | |||
| private <T> T exec(OrderBase order, String cmdName, Class<T> resultType) throws Exception { | |||
| // 将Order对象转为json | |||
| String requestJson = JSON.toJSONString(order); | |||
| System.out.println("方法名称: " + cmdName + ", 请求报文: " + requestJson); | |||
| // 生成签名 | |||
| String sign = CertificateUtils.signToBase64(requestJson.getBytes("UTF-8"), keyStorePath, alias, password); | |||
| System.out.println("方法名称: " + cmdName + ", 签名: " + sign); | |||
| // 调用接口 | |||
| Map<String, String> params = new HashMap<>(); | |||
| params.put("appCode", appCode); | |||
| params.put("cmdName", cmdName); | |||
| params.put("sign", URLEncoder.encode(sign,"UTF-8")); | |||
| String responseJson = HttpUtil.doPost(apiUrl, params, requestJson); | |||
| System.out.println("方法名称: " + cmdName + ", 响应报文: " + responseJson); | |||
| return JSON.parseObject(responseJson, resultType); | |||
| } | |||
| } | |||
| @@ -0,0 +1,454 @@ | |||
| package com.chinaeinv.einv.order.dto; | |||
| import java.math.BigDecimal; | |||
| import java.util.List; | |||
| /** | |||
| * 开票请求中的发票信息 | |||
| */ | |||
| public class Invoice { | |||
| /** | |||
| * 订单编号 | |||
| */ | |||
| private String orderNo; | |||
| /** | |||
| * 子订单编号 | |||
| */ | |||
| private String subOrderNo; | |||
| /** | |||
| * 店铺名称 | |||
| */ | |||
| private String shopName; | |||
| /** | |||
| * 店铺编号 | |||
| */ | |||
| private String shopCode; | |||
| /** | |||
| * 销货方纳税人识别号 | |||
| */ | |||
| private String taxpayerCode; | |||
| /** | |||
| * 销货方名称 | |||
| */ | |||
| private String taxpayerName; | |||
| /** | |||
| * 销货方地址 | |||
| */ | |||
| private String taxpayerAddress; | |||
| /** | |||
| * 销货方电话 | |||
| */ | |||
| private String taxpayerTel; | |||
| /** | |||
| * 销货方开户银行 | |||
| */ | |||
| private String taxpayerBankName; | |||
| /** | |||
| * 销货方银行账号 | |||
| */ | |||
| private String taxpayerBankAccount; | |||
| /** | |||
| * 购货方纳税人识别号 | |||
| */ | |||
| private String customerCode; | |||
| /** | |||
| * 购货方名称,即发票抬头 | |||
| */ | |||
| private String customerName; | |||
| /** | |||
| * 购货方地址 | |||
| */ | |||
| private String customerAddress; | |||
| /** | |||
| * 购货方电话 | |||
| */ | |||
| private String customerTel; | |||
| /** | |||
| * 购货方开户银行 | |||
| */ | |||
| private String customerBankName; | |||
| /** | |||
| * 购货方银行账号 | |||
| */ | |||
| private String customerBankAccount; | |||
| /** | |||
| * 发票代码+发票号码 | |||
| */ | |||
| private String code; | |||
| /** | |||
| * 校验码 | |||
| */ | |||
| private String checkCode; | |||
| /** | |||
| * 税控码 | |||
| */ | |||
| private String fiscalCode; | |||
| /** | |||
| * 发票状态 | |||
| * 正常:1 | |||
| * 红冲:3 | |||
| * 被红冲:4 | |||
| */ | |||
| private String status; | |||
| /** | |||
| * 生成时间 | |||
| */ | |||
| private String generateTime; | |||
| /** | |||
| * 税价合计金额 | |||
| */ | |||
| private BigDecimal totalAmount; | |||
| /** | |||
| * 不含税金额 | |||
| */ | |||
| private BigDecimal noTaxAmount; | |||
| /** | |||
| * 税额 | |||
| */ | |||
| private BigDecimal taxAmount; | |||
| /** | |||
| * 开票人 | |||
| */ | |||
| private String drawer; | |||
| /** | |||
| * 收款人 | |||
| */ | |||
| private String payee; | |||
| /** | |||
| * 复核人 | |||
| */ | |||
| private String reviewer; | |||
| /** | |||
| * 发票备注 | |||
| */ | |||
| private String remark; | |||
| /** | |||
| * 未签名PDF下载URL | |||
| */ | |||
| private String pdfUnsignedUrl; | |||
| /** | |||
| * 查看URL | |||
| */ | |||
| private String viewUrl; | |||
| /** | |||
| * 关联发票编号 | |||
| */ | |||
| private String relatedCode; | |||
| /** | |||
| * 作废/红冲原因 | |||
| */ | |||
| private String validReason; | |||
| /** | |||
| * 作废/红冲时间 | |||
| */ | |||
| private String validTime; | |||
| /** | |||
| * 项目明细 | |||
| */ | |||
| private List<InvoiceItem> items; | |||
| public String getOrderNo() { | |||
| return orderNo; | |||
| } | |||
| public void setOrderNo(String orderNo) { | |||
| this.orderNo = orderNo; | |||
| } | |||
| public String getSubOrderNo() { | |||
| return subOrderNo; | |||
| } | |||
| public void setSubOrderNo(String subOrderNo) { | |||
| this.subOrderNo = subOrderNo; | |||
| } | |||
| public String getShopName() { | |||
| return shopName; | |||
| } | |||
| public void setShopName(String shopName) { | |||
| this.shopName = shopName; | |||
| } | |||
| public String getShopCode() { | |||
| return shopCode; | |||
| } | |||
| public void setShopCode(String shopCode) { | |||
| this.shopCode = shopCode; | |||
| } | |||
| public String getTaxpayerCode() { | |||
| return taxpayerCode; | |||
| } | |||
| public void setTaxpayerCode(String taxpayerCode) { | |||
| this.taxpayerCode = taxpayerCode; | |||
| } | |||
| public String getTaxpayerName() { | |||
| return taxpayerName; | |||
| } | |||
| public void setTaxpayerName(String taxpayerName) { | |||
| this.taxpayerName = taxpayerName; | |||
| } | |||
| public String getTaxpayerAddress() { | |||
| return taxpayerAddress; | |||
| } | |||
| public void setTaxpayerAddress(String taxpayerAddress) { | |||
| this.taxpayerAddress = taxpayerAddress; | |||
| } | |||
| public String getTaxpayerTel() { | |||
| return taxpayerTel; | |||
| } | |||
| public void setTaxpayerTel(String taxpayerTel) { | |||
| this.taxpayerTel = taxpayerTel; | |||
| } | |||
| public String getTaxpayerBankName() { | |||
| return taxpayerBankName; | |||
| } | |||
| public void setTaxpayerBankName(String taxpayerBankName) { | |||
| this.taxpayerBankName = taxpayerBankName; | |||
| } | |||
| public String getTaxpayerBankAccount() { | |||
| return taxpayerBankAccount; | |||
| } | |||
| public void setTaxpayerBankAccount(String taxpayerBankAccount) { | |||
| this.taxpayerBankAccount = taxpayerBankAccount; | |||
| } | |||
| public String getCustomerCode() { | |||
| return customerCode; | |||
| } | |||
| public void setCustomerCode(String customerCode) { | |||
| this.customerCode = customerCode; | |||
| } | |||
| public String getCustomerName() { | |||
| return customerName; | |||
| } | |||
| public void setCustomerName(String customerName) { | |||
| this.customerName = customerName; | |||
| } | |||
| public String getCustomerAddress() { | |||
| return customerAddress; | |||
| } | |||
| public void setCustomerAddress(String customerAddress) { | |||
| this.customerAddress = customerAddress; | |||
| } | |||
| public String getCustomerTel() { | |||
| return customerTel; | |||
| } | |||
| public void setCustomerTel(String customerTel) { | |||
| this.customerTel = customerTel; | |||
| } | |||
| public String getCustomerBankName() { | |||
| return customerBankName; | |||
| } | |||
| public void setCustomerBankName(String customerBankName) { | |||
| this.customerBankName = customerBankName; | |||
| } | |||
| public String getCustomerBankAccount() { | |||
| return customerBankAccount; | |||
| } | |||
| public void setCustomerBankAccount(String customerBankAccount) { | |||
| this.customerBankAccount = customerBankAccount; | |||
| } | |||
| public String getCode() { | |||
| return code; | |||
| } | |||
| public void setCode(String code) { | |||
| this.code = code; | |||
| } | |||
| public String getCheckCode() { | |||
| return checkCode; | |||
| } | |||
| public void setCheckCode(String checkCode) { | |||
| this.checkCode = checkCode; | |||
| } | |||
| public String getFiscalCode() { | |||
| return fiscalCode; | |||
| } | |||
| public void setFiscalCode(String fiscalCode) { | |||
| this.fiscalCode = fiscalCode; | |||
| } | |||
| public String getStatus() { | |||
| return status; | |||
| } | |||
| public void setStatus(String status) { | |||
| this.status = status; | |||
| } | |||
| public String getGenerateTime() { | |||
| return generateTime; | |||
| } | |||
| public void setGenerateTime(String generateTime) { | |||
| this.generateTime = generateTime; | |||
| } | |||
| public BigDecimal getTotalAmount() { | |||
| return totalAmount; | |||
| } | |||
| public void setTotalAmount(BigDecimal totalAmount) { | |||
| this.totalAmount = totalAmount; | |||
| } | |||
| public BigDecimal getNoTaxAmount() { | |||
| return noTaxAmount; | |||
| } | |||
| public void setNoTaxAmount(BigDecimal noTaxAmount) { | |||
| this.noTaxAmount = noTaxAmount; | |||
| } | |||
| public BigDecimal getTaxAmount() { | |||
| return taxAmount; | |||
| } | |||
| public void setTaxAmount(BigDecimal taxAmount) { | |||
| this.taxAmount = taxAmount; | |||
| } | |||
| public String getDrawer() { | |||
| return drawer; | |||
| } | |||
| public void setDrawer(String drawer) { | |||
| this.drawer = drawer; | |||
| } | |||
| public String getPayee() { | |||
| return payee; | |||
| } | |||
| public void setPayee(String payee) { | |||
| this.payee = payee; | |||
| } | |||
| public String getReviewer() { | |||
| return reviewer; | |||
| } | |||
| public void setReviewer(String reviewer) { | |||
| this.reviewer = reviewer; | |||
| } | |||
| public String getRemark() { | |||
| return remark; | |||
| } | |||
| public void setRemark(String remark) { | |||
| this.remark = remark; | |||
| } | |||
| public String getPdfUnsignedUrl() { | |||
| return pdfUnsignedUrl; | |||
| } | |||
| public void setPdfUnsignedUrl(String pdfUnsignedUrl) { | |||
| this.pdfUnsignedUrl = pdfUnsignedUrl; | |||
| } | |||
| public String getViewUrl() { | |||
| return viewUrl; | |||
| } | |||
| public void setViewUrl(String viewUrl) { | |||
| this.viewUrl = viewUrl; | |||
| } | |||
| public String getRelatedCode() { | |||
| return relatedCode; | |||
| } | |||
| public void setRelatedCode(String relatedCode) { | |||
| this.relatedCode = relatedCode; | |||
| } | |||
| public String getValidReason() { | |||
| return validReason; | |||
| } | |||
| public void setValidReason(String validReason) { | |||
| this.validReason = validReason; | |||
| } | |||
| public String getValidTime() { | |||
| return validTime; | |||
| } | |||
| public void setValidTime(String validTime) { | |||
| this.validTime = validTime; | |||
| } | |||
| public List<InvoiceItem> getItems() { | |||
| return items; | |||
| } | |||
| public void setItems(List<InvoiceItem> items) { | |||
| this.items = items; | |||
| } | |||
| } | |||
| @@ -0,0 +1,203 @@ | |||
| package com.chinaeinv.einv.order.dto; | |||
| import java.math.BigDecimal; | |||
| /** | |||
| * 发票项目明细 | |||
| */ | |||
| public class InvoiceItem { | |||
| /** | |||
| * 发票行性质 0 正常行、1 折扣行、2 被折扣行 | |||
| */ | |||
| private String type; | |||
| /** | |||
| * 商品编码 | |||
| */ | |||
| private String code; | |||
| /** | |||
| * 商品名称 | |||
| */ | |||
| private String name; | |||
| /** | |||
| * 规格型号 | |||
| */ | |||
| private String spec; | |||
| /** | |||
| * 含税商品单价 | |||
| */ | |||
| private BigDecimal price; | |||
| /** | |||
| * 数量 | |||
| */ | |||
| private BigDecimal quantity; | |||
| /** | |||
| * 单位 | |||
| */ | |||
| private String uom; | |||
| /** | |||
| * 税率 | |||
| */ | |||
| private BigDecimal taxRate; | |||
| /** | |||
| * 税价合计金额 | |||
| */ | |||
| private BigDecimal amount; | |||
| /** | |||
| * 不含税金额 | |||
| */ | |||
| private BigDecimal noTaxAmount; | |||
| /** | |||
| * 税额 | |||
| */ | |||
| private BigDecimal taxAmount; | |||
| /** | |||
| * 商品分类编码 | |||
| */ | |||
| private String catalogCode; | |||
| /** | |||
| * 优惠政策标识 | |||
| */ | |||
| private String preferentialPolicyFlg; | |||
| /** | |||
| * 增值税特殊管理 | |||
| */ | |||
| private String addedValueTaxFlg; | |||
| /** | |||
| * 零税率标识 | |||
| */ | |||
| private String zeroTaxRateFlg; | |||
| public String getType() { | |||
| return type; | |||
| } | |||
| public void setType(String type) { | |||
| this.type = type; | |||
| } | |||
| public String getCode() { | |||
| return code; | |||
| } | |||
| public void setCode(String code) { | |||
| this.code = code; | |||
| } | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| public void setName(String name) { | |||
| this.name = name; | |||
| } | |||
| public String getSpec() { | |||
| return spec; | |||
| } | |||
| public void setSpec(String spec) { | |||
| this.spec = spec; | |||
| } | |||
| public BigDecimal getPrice() { | |||
| return price; | |||
| } | |||
| public void setPrice(BigDecimal price) { | |||
| this.price = price; | |||
| } | |||
| public BigDecimal getQuantity() { | |||
| return quantity; | |||
| } | |||
| public void setQuantity(BigDecimal quantity) { | |||
| this.quantity = quantity; | |||
| } | |||
| public String getUom() { | |||
| return uom; | |||
| } | |||
| public void setUom(String uom) { | |||
| this.uom = uom; | |||
| } | |||
| public BigDecimal getTaxRate() { | |||
| return taxRate; | |||
| } | |||
| public void setTaxRate(BigDecimal taxRate) { | |||
| this.taxRate = taxRate; | |||
| } | |||
| public BigDecimal getAmount() { | |||
| return amount; | |||
| } | |||
| public void setAmount(BigDecimal amount) { | |||
| this.amount = amount; | |||
| } | |||
| public BigDecimal getNoTaxAmount() { | |||
| return noTaxAmount; | |||
| } | |||
| public void setNoTaxAmount(BigDecimal noTaxAmount) { | |||
| this.noTaxAmount = noTaxAmount; | |||
| } | |||
| public BigDecimal getTaxAmount() { | |||
| return taxAmount; | |||
| } | |||
| public void setTaxAmount(BigDecimal taxAmount) { | |||
| this.taxAmount = taxAmount; | |||
| } | |||
| public String getCatalogCode() { | |||
| return catalogCode; | |||
| } | |||
| public void setCatalogCode(String catalogCode) { | |||
| this.catalogCode = catalogCode; | |||
| } | |||
| public String getPreferentialPolicyFlg() { | |||
| return preferentialPolicyFlg; | |||
| } | |||
| public void setPreferentialPolicyFlg(String preferentialPolicyFlg) { | |||
| this.preferentialPolicyFlg = preferentialPolicyFlg; | |||
| } | |||
| public String getAddedValueTaxFlg() { | |||
| return addedValueTaxFlg; | |||
| } | |||
| public void setAddedValueTaxFlg(String addedValueTaxFlg) { | |||
| this.addedValueTaxFlg = addedValueTaxFlg; | |||
| } | |||
| public String getZeroTaxRateFlg() { | |||
| return zeroTaxRateFlg; | |||
| } | |||
| public void setZeroTaxRateFlg(String zeroTaxRateFlg) { | |||
| this.zeroTaxRateFlg = zeroTaxRateFlg; | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| package com.chinaeinv.einv.order.dto; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| /** | |||
| * “根据订单编号查询发票”方法响应和回调数据 | |||
| */ | |||
| public class InvoiceResult extends Result { | |||
| /** | |||
| * 发票列表 | |||
| */ | |||
| private List<Invoice> invoices; | |||
| public List<Invoice> getInvoices() { | |||
| return invoices; | |||
| } | |||
| public void setInvoices(List<Invoice> invoices) { | |||
| this.invoices = invoices; | |||
| } | |||
| } | |||
| @@ -0,0 +1,387 @@ | |||
| package com.chinaeinv.einv.order.dto; | |||
| import java.math.BigDecimal; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| /** | |||
| * 订单详细信息 | |||
| */ | |||
| public class Order extends OrderBase { | |||
| /** | |||
| * 用户扫码key | |||
| */ | |||
| private String scanCodeKey; | |||
| /** | |||
| * 店铺 | |||
| */ | |||
| private String shopName; | |||
| /** | |||
| * 店铺编号 | |||
| */ | |||
| private String shopCode; | |||
| /** | |||
| * 订单时间(yyyy-MM-dd HH:mm:ss) | |||
| */ | |||
| private String orderTime; | |||
| /** | |||
| * 联系人 | |||
| */ | |||
| private String contact; | |||
| /** | |||
| * 联系电话 | |||
| */ | |||
| private String contactTel; | |||
| /** | |||
| * 邮箱 | |||
| */ | |||
| private String contactMail; | |||
| /** | |||
| * 配送地址 | |||
| */ | |||
| private String shippingAddress; | |||
| /** | |||
| * 销货方名称 | |||
| */ | |||
| private String taxpayerName; | |||
| /** | |||
| * 销货方地址 | |||
| */ | |||
| private String taxpayerAddress; | |||
| /** | |||
| * 销货方电话 | |||
| */ | |||
| private String taxpayerTel; | |||
| /** | |||
| * 销货方开户银行 | |||
| */ | |||
| private String taxpayerBankName; | |||
| /** | |||
| * 销货方银行账号 | |||
| */ | |||
| private String taxpayerBankAccount; | |||
| /** | |||
| * 购货方名称,即发票抬头 | |||
| */ | |||
| private String customerName; | |||
| /** | |||
| * 购货方纳税人识别号 | |||
| */ | |||
| private String customerCode; | |||
| /** | |||
| * 购货方地址 | |||
| */ | |||
| private String customerAddress; | |||
| /** | |||
| * 购货方电话 | |||
| */ | |||
| private String customerTel; | |||
| /** | |||
| * 购货方开户银行 | |||
| */ | |||
| private String customerBankName; | |||
| /** | |||
| * 购货方银行账号 | |||
| */ | |||
| private String customerBankAccount; | |||
| /** | |||
| * 是否自动开票 | |||
| */ | |||
| private boolean autoBilling; | |||
| /** | |||
| * 开票人 | |||
| */ | |||
| private String drawer; | |||
| /** | |||
| * 收款人 | |||
| */ | |||
| private String payee; | |||
| /** | |||
| * 复核人 | |||
| */ | |||
| private String reviewer; | |||
| /** | |||
| * 开票金额 | |||
| */ | |||
| private BigDecimal totalAmount; | |||
| /** | |||
| * 发票备注 | |||
| */ | |||
| private String remark; | |||
| /** | |||
| * 冲红原因 | |||
| */ | |||
| private String reason; | |||
| /** | |||
| * 订单明细 | |||
| */ | |||
| private List<OrderItem> orderItems; | |||
| /** | |||
| * 扩展参数 | |||
| */ | |||
| private Map<String, String> extendedParams; | |||
| /** | |||
| * 动态参数 | |||
| */ | |||
| private Map<String, String> dynamicParams; | |||
| public String getScanCodeKey() { | |||
| return scanCodeKey; | |||
| } | |||
| public void setScanCodeKey(String scanCodeKey) { | |||
| this.scanCodeKey = scanCodeKey; | |||
| } | |||
| public String getShopName() { | |||
| return shopName; | |||
| } | |||
| public void setShopName(String shopName) { | |||
| this.shopName = shopName; | |||
| } | |||
| public String getShopCode() { | |||
| return shopCode; | |||
| } | |||
| public void setShopCode(String shopCode) { | |||
| this.shopCode = shopCode; | |||
| } | |||
| public String getOrderTime() { | |||
| return orderTime; | |||
| } | |||
| public void setOrderTime(String orderTime) { | |||
| this.orderTime = orderTime; | |||
| } | |||
| public String getContact() { | |||
| return contact; | |||
| } | |||
| public void setContact(String contact) { | |||
| this.contact = contact; | |||
| } | |||
| public String getContactTel() { | |||
| return contactTel; | |||
| } | |||
| public void setContactTel(String contactTel) { | |||
| this.contactTel = contactTel; | |||
| } | |||
| public String getContactMail() { | |||
| return contactMail; | |||
| } | |||
| public void setContactMail(String contactMail) { | |||
| this.contactMail = contactMail; | |||
| } | |||
| public String getShippingAddress() { | |||
| return shippingAddress; | |||
| } | |||
| public void setShippingAddress(String shippingAddress) { | |||
| this.shippingAddress = shippingAddress; | |||
| } | |||
| public String getTaxpayerName() { | |||
| return taxpayerName; | |||
| } | |||
| public void setTaxpayerName(String taxpayerName) { | |||
| this.taxpayerName = taxpayerName; | |||
| } | |||
| public String getTaxpayerAddress() { | |||
| return taxpayerAddress; | |||
| } | |||
| public void setTaxpayerAddress(String taxpayerAddress) { | |||
| this.taxpayerAddress = taxpayerAddress; | |||
| } | |||
| public String getTaxpayerTel() { | |||
| return taxpayerTel; | |||
| } | |||
| public void setTaxpayerTel(String taxpayerTel) { | |||
| this.taxpayerTel = taxpayerTel; | |||
| } | |||
| public String getTaxpayerBankName() { | |||
| return taxpayerBankName; | |||
| } | |||
| public void setTaxpayerBankName(String taxpayerBankName) { | |||
| this.taxpayerBankName = taxpayerBankName; | |||
| } | |||
| public String getTaxpayerBankAccount() { | |||
| return taxpayerBankAccount; | |||
| } | |||
| public void setTaxpayerBankAccount(String taxpayerBankAccount) { | |||
| this.taxpayerBankAccount = taxpayerBankAccount; | |||
| } | |||
| public String getCustomerName() { | |||
| return customerName; | |||
| } | |||
| public void setCustomerName(String customerName) { | |||
| this.customerName = customerName; | |||
| } | |||
| public String getCustomerCode() { | |||
| return customerCode; | |||
| } | |||
| public void setCustomerCode(String customerCode) { | |||
| this.customerCode = customerCode; | |||
| } | |||
| public String getCustomerAddress() { | |||
| return customerAddress; | |||
| } | |||
| public void setCustomerAddress(String customerAddress) { | |||
| this.customerAddress = customerAddress; | |||
| } | |||
| public String getCustomerTel() { | |||
| return customerTel; | |||
| } | |||
| public void setCustomerTel(String customerTel) { | |||
| this.customerTel = customerTel; | |||
| } | |||
| public String getCustomerBankName() { | |||
| return customerBankName; | |||
| } | |||
| public void setCustomerBankName(String customerBankName) { | |||
| this.customerBankName = customerBankName; | |||
| } | |||
| public String getCustomerBankAccount() { | |||
| return customerBankAccount; | |||
| } | |||
| public void setCustomerBankAccount(String customerBankAccount) { | |||
| this.customerBankAccount = customerBankAccount; | |||
| } | |||
| public boolean isAutoBilling() { | |||
| return autoBilling; | |||
| } | |||
| public void setAutoBilling(boolean autoBilling) { | |||
| this.autoBilling = autoBilling; | |||
| } | |||
| public String getDrawer() { | |||
| return drawer; | |||
| } | |||
| public void setDrawer(String drawer) { | |||
| this.drawer = drawer; | |||
| } | |||
| public String getPayee() { | |||
| return payee; | |||
| } | |||
| public void setPayee(String payee) { | |||
| this.payee = payee; | |||
| } | |||
| public String getReviewer() { | |||
| return reviewer; | |||
| } | |||
| public void setReviewer(String reviewer) { | |||
| this.reviewer = reviewer; | |||
| } | |||
| public BigDecimal getTotalAmount() { | |||
| return totalAmount; | |||
| } | |||
| public void setTotalAmount(BigDecimal totalAmount) { | |||
| this.totalAmount = totalAmount; | |||
| } | |||
| public String getRemark() { | |||
| return remark; | |||
| } | |||
| public void setRemark(String remark) { | |||
| this.remark = remark; | |||
| } | |||
| public String getReason() { | |||
| return reason; | |||
| } | |||
| public void setReason(String reason) { | |||
| this.reason = reason; | |||
| } | |||
| public List<OrderItem> getOrderItems() { | |||
| return orderItems; | |||
| } | |||
| public void setOrderItems(List<OrderItem> orderItems) { | |||
| this.orderItems = orderItems; | |||
| } | |||
| public Map<String, String> getExtendedParams() { | |||
| return extendedParams; | |||
| } | |||
| public void setExtendedParams(Map<String, String> extendedParams) { | |||
| this.extendedParams = extendedParams; | |||
| } | |||
| public Map<String, String> getDynamicParams() { | |||
| return dynamicParams; | |||
| } | |||
| public void setDynamicParams(Map<String, String> dynamicParams) { | |||
| this.dynamicParams = dynamicParams; | |||
| } | |||
| } | |||
| @@ -0,0 +1,72 @@ | |||
| package com.chinaeinv.einv.order.dto; | |||
| /** | |||
| * 订单基本信息 | |||
| */ | |||
| public class OrderBase { | |||
| /** | |||
| * 订单编号 | |||
| */ | |||
| private String orderNo; | |||
| /** | |||
| * 子订单编号 | |||
| */ | |||
| private String subOrderNo; | |||
| /** | |||
| * 销货方纳税人识别号 | |||
| */ | |||
| private String taxpayerCode; | |||
| /** | |||
| * 发票类型 | |||
| */ | |||
| private String invoiceType; | |||
| public OrderBase() { | |||
| } | |||
| public OrderBase(String orderNo, String taxpayerCode) { | |||
| this.orderNo = orderNo; | |||
| this.taxpayerCode = taxpayerCode; | |||
| } | |||
| public OrderBase(String orderNo, String subOrderNo, String taxpayerCode) { | |||
| this.orderNo = orderNo; | |||
| this.subOrderNo = subOrderNo; | |||
| this.taxpayerCode = taxpayerCode; | |||
| } | |||
| public String getOrderNo() { | |||
| return orderNo; | |||
| } | |||
| public void setOrderNo(String orderNo) { | |||
| this.orderNo = orderNo; | |||
| } | |||
| public String getSubOrderNo() { | |||
| return subOrderNo; | |||
| } | |||
| public void setSubOrderNo(String subOrderNo) { | |||
| this.subOrderNo = subOrderNo; | |||
| } | |||
| public String getTaxpayerCode() { | |||
| return taxpayerCode; | |||
| } | |||
| public void setTaxpayerCode(String taxpayerCode) { | |||
| this.taxpayerCode = taxpayerCode; | |||
| } | |||
| public String getInvoiceType() { | |||
| return invoiceType; | |||
| } | |||
| public void setInvoiceType(String invoiceType) { | |||
| this.invoiceType = invoiceType; | |||
| } | |||
| } | |||
| @@ -0,0 +1,174 @@ | |||
| package com.chinaeinv.einv.order.dto; | |||
| import java.math.BigDecimal; | |||
| public class OrderItem { | |||
| /** | |||
| * 商品编码 | |||
| */ | |||
| private String code; | |||
| /** | |||
| * 商品名称 | |||
| */ | |||
| private String name; | |||
| /** | |||
| * 规格型号 | |||
| */ | |||
| private String spec; | |||
| /** | |||
| * 商品单价 | |||
| */ | |||
| private BigDecimal price; | |||
| /** | |||
| * 数量 | |||
| */ | |||
| private BigDecimal quantity; | |||
| /** | |||
| * 单位 | |||
| */ | |||
| private String uom; | |||
| /** | |||
| * 税率 | |||
| */ | |||
| private BigDecimal taxRate; | |||
| /** | |||
| * 税价合计金额 | |||
| */ | |||
| private BigDecimal amount; | |||
| /** | |||
| * 折扣金额 | |||
| */ | |||
| private BigDecimal discountAmount; | |||
| /** | |||
| * 商品分类编码 | |||
| */ | |||
| private String catalogCode; | |||
| /** | |||
| * 优惠政策标识 | |||
| */ | |||
| private String preferentialPolicyFlg; | |||
| /** | |||
| * 增值税特殊管理 | |||
| */ | |||
| private String addedValueTaxFlg; | |||
| /** | |||
| * 零税率标识 | |||
| */ | |||
| private String zeroTaxRateFlg; | |||
| public String getCode() { | |||
| return code; | |||
| } | |||
| public void setCode(String code) { | |||
| this.code = code; | |||
| } | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| public void setName(String name) { | |||
| this.name = name; | |||
| } | |||
| public String getSpec() { | |||
| return spec; | |||
| } | |||
| public void setSpec(String spec) { | |||
| this.spec = spec; | |||
| } | |||
| public BigDecimal getPrice() { | |||
| return price; | |||
| } | |||
| public void setPrice(BigDecimal price) { | |||
| this.price = price; | |||
| } | |||
| public BigDecimal getQuantity() { | |||
| return quantity; | |||
| } | |||
| public void setQuantity(BigDecimal quantity) { | |||
| this.quantity = quantity; | |||
| } | |||
| public String getUom() { | |||
| return uom; | |||
| } | |||
| public void setUom(String uom) { | |||
| this.uom = uom; | |||
| } | |||
| public BigDecimal getTaxRate() { | |||
| return taxRate; | |||
| } | |||
| public void setTaxRate(BigDecimal taxRate) { | |||
| this.taxRate = taxRate; | |||
| } | |||
| public BigDecimal getAmount() { | |||
| return amount; | |||
| } | |||
| public void setAmount(BigDecimal amount) { | |||
| this.amount = amount; | |||
| } | |||
| public BigDecimal getDiscountAmount() { | |||
| return discountAmount; | |||
| } | |||
| public void setDiscountAmount(BigDecimal discountAmount) { | |||
| this.discountAmount = discountAmount; | |||
| } | |||
| public String getCatalogCode() { | |||
| return catalogCode; | |||
| } | |||
| public void setCatalogCode(String catalogCode) { | |||
| this.catalogCode = catalogCode; | |||
| } | |||
| public String getPreferentialPolicyFlg() { | |||
| return preferentialPolicyFlg; | |||
| } | |||
| public void setPreferentialPolicyFlg(String preferentialPolicyFlg) { | |||
| this.preferentialPolicyFlg = preferentialPolicyFlg; | |||
| } | |||
| public String getAddedValueTaxFlg() { | |||
| return addedValueTaxFlg; | |||
| } | |||
| public void setAddedValueTaxFlg(String addedValueTaxFlg) { | |||
| this.addedValueTaxFlg = addedValueTaxFlg; | |||
| } | |||
| public String getZeroTaxRateFlg() { | |||
| return zeroTaxRateFlg; | |||
| } | |||
| public void setZeroTaxRateFlg(String zeroTaxRateFlg) { | |||
| this.zeroTaxRateFlg = zeroTaxRateFlg; | |||
| } | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| package com.chinaeinv.einv.order.dto; | |||
| /** | |||
| * 异步方法响应和“取消订单”方法响应 | |||
| */ | |||
| public class Result { | |||
| /** | |||
| * 处理结果代码 | |||
| */ | |||
| private String code; | |||
| /** | |||
| * 处理结果消息 | |||
| */ | |||
| private String message; | |||
| public String getCode() { | |||
| return code; | |||
| } | |||
| public void setCode(String code) { | |||
| this.code = code; | |||
| } | |||
| public String getMessage() { | |||
| return message; | |||
| } | |||
| public void setMessage(String message) { | |||
| this.message = message; | |||
| } | |||
| } | |||
| @@ -0,0 +1,888 @@ | |||
| package com.chinaeinv.einv.util; | |||
| import javax.crypto.Cipher; | |||
| import java.io.*; | |||
| import java.nio.MappedByteBuffer; | |||
| import java.nio.channels.FileChannel; | |||
| import java.security.KeyStore; | |||
| import java.security.PrivateKey; | |||
| import java.security.PublicKey; | |||
| import java.security.Signature; | |||
| import java.security.cert.Certificate; | |||
| import java.security.cert.CertificateException; | |||
| import java.security.cert.CertificateFactory; | |||
| import java.security.cert.X509Certificate; | |||
| import java.util.Base64; | |||
| import java.util.Date; | |||
| /** | |||
| * <p> | |||
| * 数字签名/加密解密工具包 | |||
| * </p> | |||
| */ | |||
| public class CertificateUtils { | |||
| /** | |||
| * Java密钥库(Java 密钥库,JKS)KEY_STORE | |||
| */ | |||
| public static final String KEY_STORE = "JKS"; | |||
| public static final String X509 = "X.509"; | |||
| /** | |||
| * 文件读取缓冲区大小 | |||
| */ | |||
| private static final int CACHE_SIZE = 2048; | |||
| /** | |||
| * 最大文件加密块 | |||
| */ | |||
| private static final int MAX_ENCRYPT_BLOCK = 117; | |||
| /** | |||
| * 最大文件解密块 | |||
| */ | |||
| private static final int MAX_DECRYPT_BLOCK = 128; | |||
| /** | |||
| * <p> | |||
| * 根据密钥库获得私钥 | |||
| * </p> | |||
| * | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| static PrivateKey getPrivateKey(String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| KeyStore keyStore = getKeyStore(keyStorePath, password); | |||
| PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray()); | |||
| return privateKey; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 获得密钥库 | |||
| * </p> | |||
| * | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| private static KeyStore getKeyStore(String keyStorePath, String password) | |||
| throws Exception { | |||
| FileInputStream in = new FileInputStream(keyStorePath); | |||
| KeyStore keyStore = KeyStore.getInstance(KEY_STORE); | |||
| keyStore.load(in, password.toCharArray()); | |||
| in.close(); | |||
| return keyStore; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 根据证书获得公钥 | |||
| * </p> | |||
| * | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| static PublicKey getPublicKey(String certificatePath) throws CertificateException, IOException { | |||
| Certificate certificate = getCertificate(certificatePath); | |||
| PublicKey publicKey = certificate.getPublicKey(); | |||
| return publicKey; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 根据证书获得公钥 | |||
| * </p> | |||
| * | |||
| * @param certificateInStream 证书输入流 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| static PublicKey getPublicKey(InputStream certificateInStream) throws CertificateException, IOException { | |||
| Certificate certificate = getCertificate(certificateInStream); | |||
| PublicKey publicKey = certificate.getPublicKey(); | |||
| return publicKey; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 获得证书 | |||
| * </p> | |||
| * | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| private static Certificate getCertificate(String certificatePath) throws CertificateException, IOException { | |||
| CertificateFactory certificateFactory = CertificateFactory.getInstance(X509); | |||
| FileInputStream in = new FileInputStream(certificatePath); | |||
| Certificate certificate = certificateFactory.generateCertificate(in); | |||
| in.close(); | |||
| return certificate; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 获得证书 | |||
| * </p> | |||
| * | |||
| * @param certificateInStream 证书输入流 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| private static Certificate getCertificate(InputStream certificateInStream) throws CertificateException, IOException { | |||
| CertificateFactory certificateFactory = CertificateFactory.getInstance(X509); | |||
| Certificate certificate = certificateFactory.generateCertificate(certificateInStream); | |||
| certificateInStream.close(); | |||
| return certificate; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 根据密钥库获得证书 | |||
| * </p> | |||
| * | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| private static Certificate getCertificate(String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| KeyStore keyStore = getKeyStore(keyStorePath, password); | |||
| Certificate certificate = keyStore.getCertificate(alias); | |||
| return certificate; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 私钥加密 | |||
| * </p> | |||
| * | |||
| * @param data 源数据 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| // 取得私钥 | |||
| PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password); | |||
| Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); | |||
| System.out.print("\n=========="+privateKey.getAlgorithm()+"\n=========="); | |||
| cipher.init(Cipher.ENCRYPT_MODE, privateKey); | |||
| int inputLen = data.length; | |||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
| int offSet = 0; | |||
| byte[] cache; | |||
| int i = 0; | |||
| // 对数据分段加密 | |||
| while (inputLen - offSet > 0) { | |||
| if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { | |||
| cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); | |||
| } else { | |||
| cache = cipher.doFinal(data, offSet, inputLen - offSet); | |||
| } | |||
| out.write(cache, 0, cache.length); | |||
| i++; | |||
| offSet = i * MAX_ENCRYPT_BLOCK; | |||
| } | |||
| byte[] encryptedData = out.toByteArray(); | |||
| out.close(); | |||
| return encryptedData; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 文件私钥加密 | |||
| * </p> | |||
| * <p> | |||
| * 过大的文件可能会导致内存溢出 | |||
| * </> | |||
| * | |||
| * @param filePath 文件路径 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static byte[] encryptFileByPrivateKey(String filePath, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| byte[] data = fileToByte(filePath); | |||
| return encryptByPrivateKey(data, keyStorePath, alias, password); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 文件加密 | |||
| * </p> | |||
| * | |||
| * @param srcFilePath 源文件 | |||
| * @param destFilePath 加密后文件 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @throws Exception | |||
| */ | |||
| public static void encryptFileByPrivateKey(String srcFilePath, String destFilePath, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| // 取得私钥 | |||
| PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password); | |||
| Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); | |||
| cipher.init(Cipher.ENCRYPT_MODE, privateKey); | |||
| File srcFile = new File(srcFilePath); | |||
| FileInputStream in = new FileInputStream(srcFile); | |||
| File destFile = new File(destFilePath); | |||
| if (!destFile.getParentFile().exists()) { | |||
| destFile.getParentFile().mkdirs(); | |||
| } | |||
| destFile.createNewFile(); | |||
| OutputStream out = new FileOutputStream(destFile); | |||
| byte[] data = new byte[MAX_ENCRYPT_BLOCK]; | |||
| byte[] encryptedData; // 加密块 | |||
| while (in.read(data) != -1) { | |||
| encryptedData = cipher.doFinal(data); | |||
| out.write(encryptedData, 0, encryptedData.length); | |||
| out.flush(); | |||
| } | |||
| out.close(); | |||
| in.close(); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 文件加密成BASE64编码的字符串 | |||
| * </p> | |||
| * | |||
| * @param filePath 文件路径 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| // public static String encryptFileToBase64ByPrivateKey(String filePath, String keyStorePath, String alias, String password) | |||
| // throws Exception { | |||
| // byte[] encryptedData = encryptFileByPrivateKey(filePath, keyStorePath, alias, password); | |||
| // return Base64Utils.encode(encryptedData); | |||
| // } | |||
| /** | |||
| * <p> | |||
| * 私钥解密 | |||
| * </p> | |||
| * | |||
| * @param encryptedData 已加密数据 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static byte[] decryptByPrivateKey(byte[] encryptedData, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| // 取得私钥 | |||
| PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password); | |||
| Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); | |||
| cipher.init(Cipher.DECRYPT_MODE, privateKey); | |||
| // 解密byte数组最大长度限制: 128 | |||
| int inputLen = encryptedData.length; | |||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
| int offSet = 0; | |||
| byte[] cache; | |||
| int i = 0; | |||
| // 对数据分段解密 | |||
| while (inputLen - offSet > 0) { | |||
| if (inputLen - offSet > MAX_DECRYPT_BLOCK) { | |||
| cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); | |||
| } else { | |||
| cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); | |||
| } | |||
| out.write(cache, 0, cache.length); | |||
| i++; | |||
| offSet = i * MAX_DECRYPT_BLOCK; | |||
| } | |||
| byte[] decryptedData = out.toByteArray(); | |||
| out.close(); | |||
| return decryptedData; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 公钥加密 | |||
| * </p> | |||
| * | |||
| * @param data 源数据 | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static byte[] encryptByPublicKey(byte[] data, String certificatePath) | |||
| throws Exception { | |||
| // 取得公钥 | |||
| PublicKey publicKey = getPublicKey(certificatePath); | |||
| Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm()); | |||
| cipher.init(Cipher.ENCRYPT_MODE, publicKey); | |||
| int inputLen = data.length; | |||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
| int offSet = 0; | |||
| byte[] cache; | |||
| int i = 0; | |||
| // 对数据分段加密 | |||
| while (inputLen - offSet > 0) { | |||
| if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { | |||
| cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); | |||
| } else { | |||
| cache = cipher.doFinal(data, offSet, inputLen - offSet); | |||
| } | |||
| out.write(cache, 0, cache.length); | |||
| i++; | |||
| offSet = i * MAX_ENCRYPT_BLOCK; | |||
| } | |||
| byte[] encryptedData = out.toByteArray(); | |||
| out.close(); | |||
| return encryptedData; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 公钥解密 | |||
| * </p> | |||
| * | |||
| * @param encryptedData 已加密数据 | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static byte[] decryptByPublicKey(byte[] encryptedData, String certificatePath) | |||
| throws Exception { | |||
| PublicKey publicKey = getPublicKey(certificatePath); | |||
| Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm()); | |||
| cipher.init(Cipher.DECRYPT_MODE, publicKey); | |||
| int inputLen = encryptedData.length; | |||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
| int offSet = 0; | |||
| byte[] cache; | |||
| int i = 0; | |||
| // 对数据分段解密 | |||
| while (inputLen - offSet > 0) { | |||
| if (inputLen - offSet > MAX_DECRYPT_BLOCK) { | |||
| cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); | |||
| } else { | |||
| cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); | |||
| } | |||
| out.write(cache, 0, cache.length); | |||
| i++; | |||
| offSet = i * MAX_DECRYPT_BLOCK; | |||
| } | |||
| byte[] decryptedData = out.toByteArray(); | |||
| out.close(); | |||
| return decryptedData; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 文件解密 | |||
| * </p> | |||
| * | |||
| * @param srcFilePath 源文件 | |||
| * @param destFilePath 目标文件 | |||
| * @param certificatePath 证书存储路径 | |||
| * @throws Exception | |||
| */ | |||
| public static void decryptFileByPublicKey(String srcFilePath, String destFilePath, String certificatePath) | |||
| throws Exception { | |||
| PublicKey publicKey = getPublicKey(certificatePath); | |||
| Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm()); | |||
| cipher.init(Cipher.DECRYPT_MODE, publicKey); | |||
| File srcFile = new File(srcFilePath); | |||
| FileInputStream in = new FileInputStream(srcFile); | |||
| File destFile = new File(destFilePath); | |||
| if (!destFile.getParentFile().exists()) { | |||
| destFile.getParentFile().mkdirs(); | |||
| } | |||
| destFile.createNewFile(); | |||
| OutputStream out = new FileOutputStream(destFile); | |||
| byte[] data = new byte[MAX_DECRYPT_BLOCK]; | |||
| byte[] decryptedData; // 解密块 | |||
| while (in.read(data) != -1) { | |||
| decryptedData = cipher.doFinal(data); | |||
| out.write(decryptedData, 0, decryptedData.length); | |||
| out.flush(); | |||
| } | |||
| out.close(); | |||
| in.close(); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 生成数据签名 | |||
| * </p> | |||
| * | |||
| * @param data 源数据 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static byte[] sign(byte[] data, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| // 获得证书 | |||
| X509Certificate x509Certificate = (X509Certificate) getCertificate(keyStorePath, alias, password); | |||
| // 获取私钥 | |||
| KeyStore keyStore = getKeyStore(keyStorePath, password); | |||
| // 取得私钥 | |||
| PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray()); | |||
| // 构建签名 | |||
| Signature signature = Signature.getInstance(x509Certificate.getSigAlgName()); | |||
| signature.initSign(privateKey); | |||
| signature.update(data); | |||
| return signature.sign(); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 生成数据签名并以BASE64编码 | |||
| * </p> | |||
| * | |||
| * @param data 源数据 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static String signToBase64(byte[] data, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| return Base64.getEncoder().encodeToString(sign(data, keyStorePath, alias, password)); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 生成文件数据签名(BASE64) | |||
| * </p> | |||
| * <p> | |||
| * 需要先将文件私钥加密,再根据加密后的数据生成签名(BASE64),适用于小文件 | |||
| * </p> | |||
| * | |||
| * @param filePath 源文件 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static String signFileToBase64WithEncrypt(String filePath, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| byte[] encryptedData = encryptFileByPrivateKey(filePath, keyStorePath, alias, password); | |||
| return signToBase64(encryptedData, keyStorePath, alias, password); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 生成文件签名 | |||
| * </p> | |||
| * <p> | |||
| * 注意:<br> | |||
| * 方法中使用了FileChannel,其巨大Bug就是不会释放文件句柄,导致签名的文件无法操作(移动或删除等)<br> | |||
| * 该方法已被generateFileSign取代 | |||
| * </p> | |||
| * | |||
| * @param filePath 文件路径 | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| @Deprecated | |||
| public static byte[] signFile(String filePath, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| byte[] sign = new byte[0]; | |||
| // 获得证书 | |||
| X509Certificate x509Certificate = (X509Certificate) getCertificate(keyStorePath, alias, password); | |||
| // 获取私钥 | |||
| KeyStore keyStore = getKeyStore(keyStorePath, password); | |||
| // 取得私钥 | |||
| PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray()); | |||
| // 构建签名 | |||
| Signature signature = Signature.getInstance(x509Certificate.getSigAlgName()); | |||
| signature.initSign(privateKey); | |||
| File file = new File(filePath); | |||
| if (file.exists()) { | |||
| FileInputStream in = new FileInputStream(file); | |||
| FileChannel fileChannel = in.getChannel(); | |||
| MappedByteBuffer byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); | |||
| signature.update(byteBuffer); | |||
| fileChannel.close(); | |||
| in.close(); | |||
| sign = signature.sign(); | |||
| } | |||
| return sign; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 生成文件数字签名 | |||
| * </p> | |||
| * | |||
| * @param filePath | |||
| * @param keyStorePath | |||
| * @param alias | |||
| * @param password | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static byte[] generateFileSign(String filePath, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| byte[] sign = new byte[0]; | |||
| // 获得证书 | |||
| X509Certificate x509Certificate = (X509Certificate) getCertificate(keyStorePath, alias, password); | |||
| // 获取私钥 | |||
| KeyStore keyStore = getKeyStore(keyStorePath, password); | |||
| // 取得私钥 | |||
| PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray()); | |||
| // 构建签名 | |||
| Signature signature = Signature.getInstance(x509Certificate.getSigAlgName()); | |||
| signature.initSign(privateKey); | |||
| File file = new File(filePath); | |||
| if (file.exists()) { | |||
| FileInputStream in = new FileInputStream(file); | |||
| byte[] cache = new byte[CACHE_SIZE]; | |||
| int nRead = 0; | |||
| while ((nRead = in.read(cache)) != -1) { | |||
| signature.update(cache, 0, nRead); | |||
| } | |||
| in.close(); | |||
| sign = signature.sign(); | |||
| } | |||
| return sign; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 文件签名成BASE64编码字符串 | |||
| * </p> | |||
| * | |||
| * @param filePath | |||
| * @param keyStorePath | |||
| * @param alias | |||
| * @param password | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static String signFileToBase64(String filePath, String keyStorePath, String alias, String password) | |||
| throws Exception { | |||
| return Base64.getEncoder().encodeToString(generateFileSign(filePath, keyStorePath, alias, password)); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 验证签名 | |||
| * </p> | |||
| * | |||
| * @param data 已加密数据 | |||
| * @param sign 数据签名[BASE64] | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static boolean verifySign(byte[] data, String sign, String certificatePath) | |||
| throws Exception { | |||
| // 获得证书 | |||
| X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath); | |||
| // 获得公钥 | |||
| PublicKey publicKey = x509Certificate.getPublicKey(); | |||
| // 构建签名 | |||
| Signature signature = Signature.getInstance(x509Certificate.getSigAlgName()); | |||
| signature.initVerify(publicKey); | |||
| signature.update(data); | |||
| return signature.verify(Base64.getDecoder().decode(sign)); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 验证签名 | |||
| * </p> | |||
| * | |||
| * @param data 已加密数据 | |||
| * @param sign 数据签名[BASE64] | |||
| * @param certificate 证书 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static boolean verifySign(byte[] data, String sign, byte[] certificate) | |||
| throws Exception { | |||
| // 获得证书 | |||
| X509Certificate x509Certificate = (X509Certificate) getCertificate(new ByteArrayInputStream(certificate)); | |||
| // 获得公钥 | |||
| PublicKey publicKey = x509Certificate.getPublicKey(); | |||
| // 构建签名 | |||
| Signature signature = Signature.getInstance(x509Certificate.getSigAlgName()); | |||
| signature.initVerify(publicKey); | |||
| signature.update(data); | |||
| return signature.verify(Base64.getDecoder().decode(sign)); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 校验文件签名 | |||
| * </p> | |||
| * | |||
| * @param filePath | |||
| * @param sign | |||
| * @param certificatePath | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| // public static boolean validateFileSign(String filePath, String sign, String certificatePath) | |||
| // throws Exception { | |||
| // boolean result = false; | |||
| // // 获得证书 | |||
| // X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath); | |||
| // // 获得公钥 | |||
| // PublicKey publicKey = x509Certificate.getPublicKey(); | |||
| //// System.out.print("测试私钥"); | |||
| //// System.out.print(publicKey); | |||
| //// System.out.print("测试私钥"); | |||
| //// System.out.print("\n"); | |||
| // | |||
| // // 构建签名 | |||
| // Signature signature = Signature.getInstance(x509Certificate.getSigAlgName()); | |||
| // signature.initVerify(publicKey); | |||
| // File file = new File(filePath); | |||
| // if (file.exists()) { | |||
| // byte[] decodedSign = Base64Utils.decode(sign); | |||
| // FileInputStream in = new FileInputStream(file); | |||
| // byte[] cache = new byte[CACHE_SIZE]; | |||
| // int nRead = 0; | |||
| // while ((nRead = in.read(cache)) != -1) { | |||
| // signature.update(cache, 0, nRead); | |||
| // } | |||
| // in.close(); | |||
| // result = signature.verify(decodedSign); | |||
| // } | |||
| // return result; | |||
| // } | |||
| /** | |||
| * <p> | |||
| * BASE64解码->签名校验 | |||
| * </p> | |||
| * | |||
| * @param base64String BASE64编码字符串 | |||
| * @param sign 数据签名[BASE64] | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| // public static boolean verifyBase64Sign(String base64String, String sign, String certificatePath) | |||
| // throws Exception { | |||
| // byte[] data = Base64Utils.decode(base64String); | |||
| // return verifySign(data, sign, certificatePath); | |||
| // } | |||
| /** | |||
| * <p> | |||
| * BASE64解码->公钥解密-签名校验 | |||
| * </p> | |||
| * | |||
| * | |||
| * @param base64String BASE64编码字符串 | |||
| * @param sign 数据签名[BASE64] | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| // public static boolean verifyBase64SignWithDecrypt(String base64String, String sign, String certificatePath) | |||
| // throws Exception { | |||
| // byte[] encryptedData = Base64Utils.decode(base64String); | |||
| // byte[] data = decryptByPublicKey(encryptedData, certificatePath); | |||
| // return verifySign(data, sign, certificatePath); | |||
| // } | |||
| /** | |||
| * <p> | |||
| * 文件公钥解密->签名校验 | |||
| * </p> | |||
| * | |||
| * @param encryptedFilePath 加密文件路径 | |||
| * @param sign 数字证书[BASE64] | |||
| * @param certificatePath | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| // public static boolean verifyFileSignWithDecrypt(String encryptedFilePath, String sign, String certificatePath) | |||
| // throws Exception { | |||
| // byte[] encryptedData = fileToByte(encryptedFilePath); | |||
| // byte[] data = decryptByPublicKey(encryptedData, certificatePath); | |||
| // return verifySign(data, sign, certificatePath); | |||
| // } | |||
| /** | |||
| * <p> | |||
| * 校验证书当前是否有效 | |||
| * </p> | |||
| * | |||
| * @param certificate 证书 | |||
| * @return | |||
| */ | |||
| public static boolean verifyCertificate(Certificate certificate) { | |||
| return verifyCertificate(new Date(), certificate); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 验证证书是否过期或无效 | |||
| * </p> | |||
| * | |||
| * @param date 日期 | |||
| * @param certificate 证书 | |||
| * @return | |||
| */ | |||
| public static boolean verifyCertificate(Date date, Certificate certificate) { | |||
| boolean isValid = true; | |||
| try { | |||
| X509Certificate x509Certificate = (X509Certificate) certificate; | |||
| x509Certificate.checkValidity(date); | |||
| } catch (Exception e) { | |||
| isValid = false; | |||
| } | |||
| return isValid; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 验证数字证书是在给定的日期是否有效 | |||
| * </p> | |||
| * | |||
| * @param date 日期 | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| */ | |||
| public static boolean verifyCertificate(Date date, String certificatePath) { | |||
| Certificate certificate; | |||
| try { | |||
| certificate = getCertificate(certificatePath); | |||
| return verifyCertificate(certificate); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| return false; | |||
| } | |||
| } | |||
| /** | |||
| * <p> | |||
| * 验证数字证书是在给定的日期是否有效 | |||
| * </p> | |||
| * | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| */ | |||
| public static boolean verifyCertificate(Date date, String keyStorePath, String alias, String password) { | |||
| Certificate certificate; | |||
| try { | |||
| certificate = getCertificate(keyStorePath, alias, password); | |||
| return verifyCertificate(certificate); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| return false; | |||
| } | |||
| } | |||
| /** | |||
| * <p> | |||
| * 验证数字证书当前是否有效 | |||
| * </p> | |||
| * | |||
| * @param keyStorePath 密钥库存储路径 | |||
| * @param alias 密钥库别名 | |||
| * @param password 密钥库密码 | |||
| * @return | |||
| */ | |||
| public static boolean verifyCertificate(String keyStorePath, String alias, String password) { | |||
| return verifyCertificate(new Date(), keyStorePath, alias, password); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 验证数字证书当前是否有效 | |||
| * </p> | |||
| * | |||
| * @param certificatePath 证书存储路径 | |||
| * @return | |||
| */ | |||
| public static boolean verifyCertificate(String certificatePath) { | |||
| return verifyCertificate(new Date(), certificatePath); | |||
| } | |||
| /** | |||
| * <p> | |||
| * 文件转换为byte数组 | |||
| * </p> | |||
| * | |||
| * @param filePath 文件路径 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static byte[] fileToByte(String filePath) throws Exception { | |||
| byte[] data = new byte[0]; | |||
| File file = new File(filePath); | |||
| if (file.exists()) { | |||
| FileInputStream in = new FileInputStream(file); | |||
| ByteArrayOutputStream out = new ByteArrayOutputStream(2048); | |||
| byte[] cache = new byte[CACHE_SIZE]; | |||
| int nRead = 0; | |||
| while ((nRead = in.read(cache)) != -1) { | |||
| out.write(cache, 0, nRead); | |||
| out.flush(); | |||
| } | |||
| out.close(); | |||
| in.close(); | |||
| data = out.toByteArray(); | |||
| } | |||
| return data; | |||
| } | |||
| /** | |||
| * <p> | |||
| * 二进制数据写文件 | |||
| * </p> | |||
| * | |||
| * @param bytes 二进制数据 | |||
| * @param filePath 文件生成目录 | |||
| */ | |||
| public static void byteArrayToFile(byte[] bytes, String filePath) throws Exception { | |||
| InputStream in = new ByteArrayInputStream(bytes); | |||
| File destFile = new File(filePath); | |||
| if (!destFile.getParentFile().exists()) { | |||
| destFile.getParentFile().mkdirs(); | |||
| } | |||
| destFile.createNewFile(); | |||
| OutputStream out = new FileOutputStream(destFile); | |||
| byte[] cache = new byte[CACHE_SIZE]; | |||
| int nRead = 0; | |||
| while ((nRead = in.read(cache)) != -1) { | |||
| out.write(cache, 0, nRead); | |||
| out.flush(); | |||
| } | |||
| out.close(); | |||
| in.close(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,223 @@ | |||
| package com.chinaeinv.einv.util; | |||
| import javax.net.ssl.*; | |||
| import java.io.*; | |||
| import java.net.HttpURLConnection; | |||
| import java.net.URL; | |||
| import java.net.URLEncoder; | |||
| import java.security.SecureRandom; | |||
| import java.security.cert.X509Certificate; | |||
| import java.util.Map; | |||
| import java.util.Map.Entry; | |||
| /** | |||
| * 进行http访问的基本类 | |||
| */ | |||
| public class HttpUtil { | |||
| private static final String DEFAULT_CHARSET = "UTF-8"; | |||
| private static final String METHOD_POST = "POST"; | |||
| private static final String METHOD_GET = "GET"; | |||
| private static final int CONNECTTIMEOUT = 5000; | |||
| private static final int READTIMEOUT = 5000; | |||
| private static class DefaultTrustManager implements X509TrustManager { | |||
| public X509Certificate[] getAcceptedIssuers() { | |||
| return null; | |||
| } | |||
| public void checkClientTrusted(X509Certificate[] cert, String oauthType) | |||
| throws java.security.cert.CertificateException { | |||
| } | |||
| public void checkServerTrusted(X509Certificate[] cert, String oauthType) | |||
| throws java.security.cert.CertificateException { | |||
| } | |||
| } | |||
| private static HttpURLConnection getConnection(URL url, String method) | |||
| throws IOException { | |||
| HttpURLConnection conn; | |||
| if ("https".equals(url.getProtocol())) { | |||
| SSLContext ctx; | |||
| try { | |||
| ctx = SSLContext.getInstance("TLS"); | |||
| ctx.init(new KeyManager[0], new TrustManager[] { new DefaultTrustManager() }, | |||
| new SecureRandom()); | |||
| } catch (Exception e) { | |||
| throw new IOException(e); | |||
| } | |||
| HttpsURLConnection connHttps = (HttpsURLConnection) url.openConnection(); | |||
| connHttps.setSSLSocketFactory(ctx.getSocketFactory()); | |||
| connHttps.setHostnameVerifier(new HostnameVerifier() { | |||
| public boolean verify(String hostname, SSLSession session) { | |||
| return true;// 默认都认证通过 | |||
| } | |||
| }); | |||
| conn = connHttps; | |||
| } else { | |||
| conn = (HttpURLConnection) url.openConnection(); | |||
| } | |||
| conn.setRequestMethod(method); | |||
| conn.setDoInput(true); | |||
| conn.setDoOutput(true); | |||
| conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); | |||
| conn.setRequestProperty("Connection", "Keep-Alive"); | |||
| return conn; | |||
| } | |||
| /** | |||
| * 通过get方法访问 | |||
| * | |||
| * @param url 访问的url地址 | |||
| * @param urlParams 请求需要的参数 | |||
| * @return 返回请求响应的数据 | |||
| * @throws IOException | |||
| */ | |||
| public static String doGet(String url, Map<String, String> urlParams) | |||
| throws IOException { | |||
| if (isEmpty(url)) { | |||
| throw new IllegalArgumentException("The parameter 'url' can not be null or blank."); | |||
| } | |||
| url += buildQuery(urlParams, DEFAULT_CHARSET); | |||
| HttpURLConnection conn = getConnection(new URL(url), METHOD_GET); | |||
| String s = getResponseAsString(conn); | |||
| return s; | |||
| } | |||
| /** | |||
| * | |||
| * @param url api请求的权路径url地址 | |||
| * @param urlParams 请求的参数 | |||
| * @param requestJson 请求报文 | |||
| * @return 请求响应 | |||
| * @throws IOException | |||
| */ | |||
| public static String doPost(String url, Map<String, String> urlParams, String requestJson) throws IOException { | |||
| return doPost(url, urlParams, requestJson, CONNECTTIMEOUT, READTIMEOUT); | |||
| } | |||
| /** | |||
| * | |||
| * 通过post方法请求数据 | |||
| * | |||
| * @param url 请求的url地址 | |||
| * @param urlParams 请求的参数 | |||
| * @param requestJson 请求报文 | |||
| * @param connectTimeOut 请求连接过期时间 | |||
| * @param readTimeOut 请求读取过期时间 | |||
| * @return 请求响应 | |||
| * @throws IOException | |||
| */ | |||
| public static String doPost(String url, Map<String, String> urlParams, String requestJson, | |||
| int connectTimeOut, int readTimeOut) throws IOException { | |||
| if (isEmpty(url)) { | |||
| throw new IllegalArgumentException("The parameter 'url' can not be null or blank."); | |||
| } | |||
| url += buildQuery(urlParams, DEFAULT_CHARSET); | |||
| HttpURLConnection conn = getConnection(new URL(url), METHOD_POST); | |||
| conn.setConnectTimeout(connectTimeOut); | |||
| conn.setReadTimeout(readTimeOut); | |||
| conn.getOutputStream().write(requestJson.getBytes(DEFAULT_CHARSET)); | |||
| String s = getResponseAsString(conn); | |||
| return s; | |||
| } | |||
| /** | |||
| * | |||
| * @param params 请求参数 | |||
| * @return 构建query | |||
| */ | |||
| public static String buildQuery(Map<String, String> params, String charset) throws UnsupportedEncodingException { | |||
| if (params == null || params.isEmpty()) { | |||
| return ""; | |||
| } | |||
| StringBuilder sb = new StringBuilder(); | |||
| boolean first = true; | |||
| for (Entry<String, String> entry : params.entrySet()) { | |||
| if (first) { | |||
| sb.append("?"); | |||
| first = false; | |||
| } else { | |||
| sb.append("&"); | |||
| } | |||
| String key = entry.getKey(); | |||
| String value = entry.getValue(); | |||
| if (areNotEmpty(key, value)) { | |||
| sb.append(key).append("=").append(URLEncoder.encode(value, charset)); | |||
| } | |||
| } | |||
| return sb.toString(); | |||
| } | |||
| private static String getResponseAsString(HttpURLConnection conn) throws IOException { | |||
| InputStream es = conn.getErrorStream(); | |||
| if (es == null) { | |||
| return getStreamAsString(conn.getInputStream(), DEFAULT_CHARSET); | |||
| } else { | |||
| String msg = getStreamAsString(es, DEFAULT_CHARSET); | |||
| if (isEmpty(msg)) { | |||
| throw new IOException(conn.getResponseCode() + " : " + conn.getResponseMessage()); | |||
| } else { | |||
| throw new IOException(msg); | |||
| } | |||
| } | |||
| } | |||
| private static String getStreamAsString(InputStream input, String charset) throws IOException { | |||
| StringBuilder sb = new StringBuilder(); | |||
| BufferedReader bf = null; | |||
| try { | |||
| bf = new BufferedReader(new InputStreamReader(input, charset)); | |||
| String str; | |||
| while ((str = bf.readLine()) != null) { | |||
| sb.append(str); | |||
| } | |||
| return sb.toString(); | |||
| } finally { | |||
| if (bf != null) { | |||
| bf.close(); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * 判断字符串为空 | |||
| * | |||
| * @param str 字符串信息 | |||
| * @return true or false | |||
| */ | |||
| private static boolean isEmpty(String str) { | |||
| return str == null || str.trim().length() == 0; | |||
| } | |||
| /** | |||
| * 判断字符数组,不为空 | |||
| * | |||
| * @param values 字符数组 | |||
| * @return true or false | |||
| */ | |||
| public static boolean areNotEmpty(String... values) { | |||
| if (values == null || values.length == 0) { | |||
| return false; | |||
| } | |||
| for (String value : values) { | |||
| if (isEmpty(value)) { | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| } | |||