诺诺开票
环境:.NetCore、NuonuoSDK
1 /// <summary>
2 /// 申请诺诺电子发票
3 /// </summary>
4 /// <param name="applySource">1:支付完自动开票;2:手动选择小订单开票</param>
5 /// <returns></returns>
6 public async Task ApplyEInvoice(string orderNo, Guid invoiceTitleId, int applySource)
7 {
8 try
9 {
10 var orderDetailList = await _orderDetailRepository.GetListAsync(f => f.OrderNo == orderNo);
11 if (applySource == 2)
12 {
13 orderDetailList = await _orderDetailRepository.GetListAsync(f => f.OrderDetailNo == orderNo);
14 }
15 if (orderDetailList != null)
16 {
17 var invoiceTitle = await _invoiceTitleRepository.GetAsync(invoiceTitleId);
18 if (invoiceTitle != null)
19 {
20 foreach (var orderDetail in orderDetailList)
21 {
22 var fpkjModel = _fpkjRequestOptions.Value;
23 fpkjModel.buyerName = invoiceTitle.Name;
24 fpkjModel.buyerTaxNum = invoiceTitle.TaxNumber;
25 fpkjModel.buyerTel = invoiceTitle.Mobile;
26 fpkjModel.buyerAddress = invoiceTitle.Address;
27 fpkjModel.buyerAccount = invoiceTitle.Bank + invoiceTitle.BankNumber;
28 fpkjModel.orderNo = orderDetail.OrderDetailNo;
29 fpkjModel.invoiceDate = orderDetail.CreationTime.ToString("yyyy-MM-dd HH:mm:ss");
30 fpkjModel.buyerPhone = invoiceTitle.SendPhoneNumber; //购方手机(pushMode为1或2时,此项必填
31 fpkjModel.email = invoiceTitle.SendEmailAddress; //推送邮箱 推送邮箱(pushMode为0或2时,此项为必填,同时受企业资质是否必填控制)
32 fpkjModel.pushMode = "2"; //推送方式:-1;不推送;0;邮箱;1;手机(默认); 2;邮箱、手机
33 fpkjModel.invoiceType = "1"; //蓝票
34 fpkjModel.invoiceLine = "pc"; //电子发票(普通发票)-即数电普票(电子)
35 fpkjModel.invoiceDetail = new List<InvoiceDetail>()
36 {
37 new InvoiceDetail()
38 {
39 taxIncludedAmount = orderDetail.OrderAmount.ToString(),
40 withTaxFlag = "1",
41 num = "1"
42 },
43 };
44
45 //string str = await _openInvoiceManager.GetEInvoiceTokenAsync();
46 //var pxResponseResult = JsonConvert.DeserializeObject<PxResponseResult<JYEInvoiceTokenResponse>>(str);
47 //var tokenInfo = pxResponseResult.Data;
48
49 (string invoiceSerialNum, string message) openInvoiceResponse = _openInvoiceManager.OpenInvoice(fpkjModel, fpkjModel.salerTaxNum);
50 Logger.LogInformation("申请诺诺电子发票:流水号:" + openInvoiceResponse.invoiceSerialNum + ",描述:" + openInvoiceResponse.message);
51 if (!string.IsNullOrEmpty(openInvoiceResponse.invoiceSerialNum) || openInvoiceResponse.message == "订单编号或流水号不能重复")
52 {
53 var openInvoice = await _openInvoiceRepository.FirstOrDefaultAsync(f => f.OrderNo == orderDetail.OrderDetailNo);
54 if (openInvoice == null)
55 {
56 OpenInvoiceRecord openInvoiceRecord = new OpenInvoiceRecord()
57 {
58 InvoiceNature = invoiceTitle.InvoiceNature,
59 InvoiceStatus = (EnumInvoiceStatus)20,
60 OrderNo = orderDetail.OrderDetailNo,
61 PayAmount = orderDetail.OrderAmount,
62 PurchaserInvoiceTitle = invoiceTitle.Name,
63 PurchaserIdTaxpayer = invoiceTitle.TaxNumber,
64 PurchaserAddressMobile = invoiceTitle.Mobile,
65 PurchaserBank = invoiceTitle.Bank,
66 PurchaserBankNumber = invoiceTitle.BankNumber,
67 UserEmail = invoiceTitle.SendEmailAddress,
68 UserMobile = invoiceTitle.SendPhoneNumber,
69 SerialNo = openInvoiceResponse.invoiceSerialNum
70 };
71 openInvoice = await _openInvoiceRepository.InsertAsync(openInvoiceRecord, true);
72 }
73
74 //查询发票:提交成功后,返回是开票提交成功,会立即返回开票流水号,这里电子票的状态不是最终状态,如果使用流水号查询开票信息,有可能是开票中状态
75 var query = QueryInvoiceAsync(openInvoice, fpkjModel.salerTaxNum);
76 }
77 }
78 }
79 else
80 {
81 Logger.LogInformation("申请诺诺电子发票:未查询到发票抬头信息");
82 }
83 }
84 else
85 {
86 Logger.LogInformation("申请诺诺电子发票:未查询到订单信息");
87 }
88 }
89 catch (Exception ex)
90 {
91 Logger.LogInformation("申请诺诺电子发票:" + ex.Message);
92 }
93 }
94
95 /// <summary>
96 /// 查询发票,完善发票信息
97 /// </summary>
98 private async Task QueryInvoiceAsync(OpenInvoiceRecord invoice, string taxNum)
99 {
100 // 等待 5 秒钟
101 await Task.Delay(TimeSpan.FromSeconds(10));
102 Logger.LogInformation($"查询发票信息:{JsonConvert.SerializeObject(invoice)}");
103 string fpckMsg = "查询发票信息";
104 while (!string.IsNullOrEmpty(fpckMsg))
105 {
106 List<string> serialNos = new List<string>();
107 if (!string.IsNullOrEmpty(invoice.SerialNo))
108 {
109 serialNos.Add(invoice.SerialNo);
110 }
111 List<string> orderNos = new List<string>()
112 {
113 invoice.OrderNo
114 };
115
116 (List<InvoicePdf> pdfList, string msg) fpInfoList = _openInvoiceManager.QueryInvoice(
117 new FpcxRequest()
118 {
119 serialNos = serialNos,
120 orderNos = orderNos
121 },
122 taxNum);
123 fpckMsg = fpInfoList.msg;
124
125 if (fpInfoList.pdfList != null && string.IsNullOrEmpty(fpckMsg))
126 {
127 Logger.LogInformation($"查询发票信息:pdfList:{JsonConvert.SerializeObject(fpInfoList.pdfList)}");
128 InvoicePdf invoicePdf = fpInfoList.pdfList.FirstOrDefault<InvoicePdf>();
129 if (!string.IsNullOrEmpty(invoicePdf.URL))
130 {
131 invoice.InvoiceUrl = invoicePdf.URL;
132 invoice.InvoiceCode = invoicePdf.FPDM;
133 invoice.InvoiceNo = invoicePdf.FPHM;
134 invoice.SerialNo = invoicePdf.FPLSH;
135 invoice.CheckCode = invoicePdf.JYM;
136 invoice.InvoiceStatus = (EnumInvoiceStatus)2;
137 invoice.InvoiceTime = DateTimeHelper.StampToDateTime(invoicePdf.KPRQ.ToString());
138
139 var temp = await _openInvoiceRepository.UpdateAsync(invoice);
140 if (temp != null)
141 {
142 return;
143 }
144 }
145 }
146 else
147 {
148 Logger.LogInformation($"查询发票信息:描述:{fpInfoList.msg},{JsonConvert.SerializeObject(fpInfoList.pdfList)}");
149 }
150 }
151 }
1 /// <summary>
2 /// 诺诺开放平台开票
3 /// </summary>
4 /// <returns></returns>
5 public (string invoiceSerialNum, string message) OpenInvoice(FpkjRequest fpkj, string taxNum)
6 {
7 foreach (var item in fpkj.invoiceDetail)
8 {
9 var sl = Convert.ToDouble(item.taxRate);
10 var hsje = Convert.ToDouble(item.taxIncludedAmount);
11 var xmdj = 0.00;//项目单价
12 var se = 0.00;//税额
13 var bhsje = 0.00;//不含税金额
14 if (item.withTaxFlag == "1")
15 {
16 xmdj = hsje / Convert.ToInt32(item.num);//项目单价
17 se = Math.Round(hsje * sl / (1 + sl), 2);
18 bhsje = hsje - se;
19 }
20 else
21 {
22 xmdj = hsje / Convert.ToInt32(item.num) / (1 + sl);
23 se = Math.Round(xmdj * Convert.ToInt32(item.num) * sl, 2);
24 bhsje = hsje - se;
25 }
26 item.tax = se.ToString();
27 item.taxExcludedAmount = bhsje.ToString();
28 item.price = xmdj.ToString();
29 }
30
31 string jsonStr = JsonConvert.SerializeObject(fpkj);
32 string content = "{";
33 content += "\"order\":";
34 content += jsonStr;
35 content += " }";
36 Logger.LogInformation($"诺诺开放平台开票接口请求:{content}");
37 var invoiceConfigDto = _invoiceConfigOptions.Value;
38 var returnStr = NuoNuoSdkHelper.sendRequest("nuonuo.OpeMplatform.requestBillingNew", content, taxNum, invoiceConfigDto);
39 Logger.LogInformation($"诺诺开放平台开票接口返回:{returnStr}");
40 var FpkjReturnModel = JsonConvert.DeserializeObject<FpkjResponse>(returnStr);
41 var invoiceSerialNum = "";
42 if (FpkjReturnModel.code == "E0000")
43 {
44 var resultSerialNumModel = JsonConvert.DeserializeObject<resultSerialNum>(JsonConvert.SerializeObject(FpkjReturnModel.result));
45 invoiceSerialNum = resultSerialNumModel.invoiceSerialNum;
46 }
47
48 return (invoiceSerialNum, FpkjReturnModel.describe);
49 }
50
51 /// <summary>
52 /// 诺诺开放平台发票查询
53 /// </summary>
54 /// <returns></returns>
55 public (List<InvoicePdf> pdfList, string msg) QueryInvoice(FpcxRequest fpcx, string taxNum)
56 {
57 string content = JsonConvert.SerializeObject(fpcx);
58 Logger.LogInformation($"诺诺开放平台发票查询接口请求:{content}");
59 var invoiceConfigDto = _invoiceConfigOptions.Value;
60 var returnStr = NuoNuoSdkHelper.sendRequest("nuonuo.OpeMplatform.queryInvoiceResult", content, taxNum, invoiceConfigDto);
61 Logger.LogInformation($"诺诺开放平台发票查询接口返回:{returnStr},参数:{content}---{taxNum}---{JsonConvert.SerializeObject(invoiceConfigDto)}");
62
63 string msg = "";
64 List<InvoicePdf> dataList = new List<InvoicePdf>();
65 var FpcxReturnModel = JsonConvert.DeserializeObject<FpcxResponse>(returnStr);
66 if (FpcxReturnModel.code == "E0000")
67 {
68 var fpInfo = FpcxReturnModel.result;
69 if (fpInfo.Count < 1)
70 {
71 msg = "发票查询:未查询到发票数据";
72 }
73 else
74 {
75 foreach (var item in fpInfo)
76 {
77 var pdfInfo = new InvoicePdf();
78 pdfInfo.DDLSH = item.orderNo;
79 pdfInfo.FPLSH = item.serialNo;
80 pdfInfo.FPDM = item.invoiceCode;
81 pdfInfo.FPHM = item.invoiceNo;
82 pdfInfo.KPRQ = item.invoiceTime;
83 pdfInfo.URL = item.pdfUrl;
84 pdfInfo.JYM = item.checkCode;
85 pdfInfo.status = item.status;
86 pdfInfo.statusMsg = item.statusMsg;
87 pdfInfo.failCause = item.failCause;
88 dataList.Add(pdfInfo);
89 }
90 }
91 }
92 else
93 {
94 msg = FpcxReturnModel.describe;
95 }
96
97 return (dataList, msg);
98 }
1 public class NuoNuoSdkHelper
2 {
3 /// <summary>
4 /// 调用诺诺开放平台API
5 /// </summary>
6 /// <returns></returns>
7 public static string sendRequest(string method, string content, string taxNum, InvoiceConfigDto _invoiceConfigDto)
8 {
9 string senid = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 32); // 唯一标识,由企业自己生成32位随机码
10 return NNOpenSDK.sendPostSyncRequest(_invoiceConfigDto.Url, senid, _invoiceConfigDto.AppKey, _invoiceConfigDto.AppSecret, _invoiceConfigDto.Token, taxNum, method, content);
11 }
12 }
1 public override void ConfigureServices(ServiceConfigurationContext context)
2 {
3 var configuration = context.Services.GetConfiguration();
4
5 ConfigureSalerInvoiceConfig(context, configuration);
6 ConfigureInvoiceConfig(context, configuration);
7 }
8
9 /// <summary>
10 /// 设置发票配置信息
11 /// </summary>
12 /// <param name="context"></param>
13 /// <param name="configuration"></param>
14 private void ConfigureInvoiceConfig(ServiceConfigurationContext context, IConfiguration configuration)
15 {
16 context.Services.Configure<InvoiceConfigDto>(options =>
17 {
18 options.AppKey = configuration["InvoiceConfigString:AppKey"];
19 options.AppSecret = configuration["InvoiceConfigString:AppSecret"];
20 options.Url = configuration["InvoiceConfigString:Url"];
21 options.Token = configuration["InvoiceConfigString:ZheJiangJianYuanShuDianToken"];
22 });
23 }
24
25 /// <summary>
26 /// 设置销售方发票配置信息
27 /// </summary>
28 /// <param name="context"></param>
29 /// <param name="configuration"></param>
30 private void ConfigureSalerInvoiceConfig(ServiceConfigurationContext context, IConfiguration configuration)
31 {
32 context.Services.Configure<FpkjRequest>(options =>
33 {
34 options.clerk = configuration["SalerInvoiceConfig:clerk"];
35 options.checker = configuration["SalerInvoiceConfig:checker"];
36 options.payee = configuration["SalerInvoiceConfig:payee"];
37 options.salerTaxNum = configuration["SalerInvoiceConfig:salerTaxNum"];
38 options.salerTel = configuration["SalerInvoiceConfig:salerTel"];
39 options.salerAddress = configuration["SalerInvoiceConfig:salerAddress"];
40 options.salerAccount = configuration["SalerInvoiceConfig:salerAccount"];
41 });
42 }
//销售方发票配置
"SalerInvoiceConfig": {
"clerk": "", //开票人
"checker": "", //复核人
"payee": "", //收款人
"salerTaxNum": "", //销售方税号(测试)
"salerTel": "", //销售方电话
"salerAddress": "", //销售方地址
"salerAccount": "" //销方银行账号和开户行地址
},
//发票配置
"InvoiceConfigString": {
"AppKey": "",
"AppSecret": "",
"Url": "https://sandbox.nuonuocs.cn/open/v1/services",
"ZheJiangJianYuanShuDianToken": "", //获取开票token密钥
"InvoiceAmountLimit": 10000 //发票最大金额限制
}