整合发票第三方--实现自助开票相关功能

整合发票第三方–实现自助开票相关功能

一、背景介绍

码农门,你们好,今天要介绍的是一个第三方的API,主要是用于自助开票或开票系统的相关功能设计。我在项目中用到的是诺诺发票,感觉功能还是比较完善的。具体网址和第三方API文档可以参考诺诺发票官网----->>>>

二、技术准备

首先,需要成为诺诺开放平台的会员,并完成认证审核(需要税号);这些工作做完之后,就可以创建应用,接入sdk进行开发。有一些注意的点需要留意:
1.开发者的APP证书(包含APP Key 和 APP Secret)需要保存好在这里插入图片描述
2.应用token选择
(1)如果是自己用,或者对于token没有相关安全限制的,可以选择固定token。但是相对安全性较低,但是开发测试比较方便。
(2)如果是企业服务使用,建议使用动态token,但是要做好token存储,这个token是有限制的,每个令牌是存在有效期(24小时)的,且令牌30天内的调用上限为50次。我在项目中,使用的是redis存储在缓存中。

三、相关参数配置

1.yaml配置文件

##发票接口参数---测试
invoice:
  # 接口地址(测试地址)
  baseURL: https://sandbox.nuonuocs.cn/open/v1/services
  # 上文所述需要保存的APP证书
  APPKey: 123456789
  APPSecret: 123456789
  # 销方税号(即开票方税号)
  taxnum: 123456789

2.代码注入配置

	//从缓存中获取token
	private String accessTokenKey = "invoice_accessToken";
    @Value("${invoice.baseURL}")
    private String baseURL;
    @Value("${invoice.APPKey}")
    private String aPPKey;
    @Value("${invoice.APPSecret}")
    private String aPPSecret;
    @Value("${invoice.taxnum}")
    private String taxnum;

四、获取应用token(此处选择的是动态token)

public String getToken() {
        try {
            Object object = cacheService.getDataWithKey(accessTokenKey);
            //Object object = null;如果放开这串代码,表明不走缓存,直接请求获取token
            if(object!=null){
                return object.toString();
            }else {
                String token = NNOpenSDK.getIntance().getMerchantToken(aPPKey,aPPSecret);
                JSONObject jsonObject = JSONObject.parseObject(token);
                String accessToken = jsonObject.getString("access_token");
                if (ObjectUtil.isNotEmpty(accessToken)){
                    cacheService.setDataWithParams(accessTokenKey,accessToken,24, TimeUnit.HOURS);
                    log.info("invoice_accessToken =========>");
                    return accessToken;
                }else {
                    throw new HbsxException("获取token失败");
                }
            }
        }  catch (Exception e){
            log.error("InvoiceSettingServiceImpl.getToken.error==>{}",e.getMessage());

            if(e instanceof XXXException){
                throw new XXXException(ErrorCode.SYSTEM_ERROR.getCode(),e.getMessage());
            }
            throw new XXXException(ErrorCode.SYSTEM_ERROR.getCode(),"系统异常,请稍后再试!");
        }
    }

五、自助开票功能

1.这是请求开具发票的一些相关请求参数
在这里插入图片描述
2.电子发票不存在作废,只能冲红,冲红也是一种开票,只不过票面类型为红票,所以接口与开蓝票接口一致,仅参数不同。
在这里插入图片描述
3.返回参数为发票流水号,开票结果可以用此参数作为唯一标识查询,需要保存。
在这里插入图片描述
4.开票接口返回的异常码需要捕捉:
在这里插入图片描述
5.示例代码

/**
     * 开具发票
     * @param invoiceSettingDTO
     * @param request
     */
    @Override
    @Transactional(rollbackFor = HbsxException.class)
    public Map<String,Object> createFP(InvoiceSettingDTO invoiceSettingDTO, HttpServletRequest request) {
        try {
            Map<String,Object> map = new HashMap<>();
            String token = getToken();
            NNOpenSDK sdk = NNOpenSDK.getIntance();
            String method = "nuonuo.electronInvoice.requestBillingNew";//API方法名
            InvoiceSetting detail = this.getDetail();
            String url = baseURL; //SDK请求地址
            JSONObject order = new JSONObject();
            JSONObject jsonObject = new JSONObject();
            order.put("buyerName",invoiceSettingDTO.getInvoiceTitle());
            //纳税人识别号
            if (ObjectUtil.isNotEmpty(invoiceSettingDTO.getTaxPayerId())&&invoiceSettingDTO.getTitleType().equals("2")){
                order.put("buyerTaxNum",invoiceSettingDTO.getTaxPayerId());
            }
            //电话
            if (ObjectUtil.isNotEmpty(invoiceSettingDTO.getCtel())&&invoiceSettingDTO.getTitleType().equals("2")){
                order.put("buyerTel",invoiceSettingDTO.getCtel());
            }
            //地址
            if (ObjectUtil.isNotEmpty(invoiceSettingDTO.getAddress())&&invoiceSettingDTO.getTitleType().equals("2")){
                order.put("buyerAddress",invoiceSettingDTO.getAddress());
            }
            //开户行和卡号
            if (ObjectUtil.isNotEmpty(invoiceSettingDTO.getBankName())&&ObjectUtil.isNotEmpty(invoiceSettingDTO.getBankaccountname())&&invoiceSettingDTO.getTitleType().equals("2")){
                order.put("buyerAccount",invoiceSettingDTO.getBankName()+invoiceSettingDTO.getBankaccountname());
            }
            order.put("salerTaxNum",detail.getTaxPayerId());
            order.put("salerTel",detail.getCtel());
            order.put("salerAddress",detail.getAddress());
            order.put("salerAccount",detail.getBankName()+detail.getBankaccountname());
            order.put("orderNo",invoiceSettingDTO.getOrderId());
            order.put("invoiceDate",invoiceSettingDTO.getOrderTime());
            order.put("checker",detail.getReviewedName());
            order.put("payee",detail.getPayeeName());
            order.put("clerk",detail.getDrawerName());
            order.put("pushMode","-1");
            order.put("invoiceType","1");
            JSONObject invoiceDetail = new JSONObject();
            //单价和数量
            invoiceDetail.put("price",invoiceSettingDTO.getPayedFee());
            invoiceDetail.put("num","1");
            //invoiceDetail.put("goodsName",invoiceSettingDTO.getCname());
            invoiceDetail.put("withTaxFlag","1");
            invoiceDetail.put("taxExcludedAmount",invoiceSettingDTO.getPayedFee());
            invoiceDetail.put("taxRate",detail.getActivityTaxrate());
            order.put("invoiceDetail",invoiceDetail);
            jsonObject.put("order",order);
            String content = jsonObject.toJSONString();
            String senid = UUID.randomUUID().toString().replace("-", ""); // 唯一标识,由企业自己生成32位随机码
            String json = sdk.sendPostSyncRequest(url, senid, aPPKey, aPPSecret, token, taxnum, method, content);
            JSONObject result = JSONObject.parseObject(json);
            String code = result.getString("code");
            System.out.println(json+"==========>>");
            System.out.println(json+"==========>>");
            System.out.println(json+"==========>>");
            if (!code.equals("E0000")){
                String errormsg = result.getString("describe");
                throw new XXXException(errormsg+",开票失败,请稍后再试");
            }else {
                JSONObject result1 = result.getJSONObject("result");
                //获取发票流水号
                String invoiceSerialNum = result1.getString("invoiceSerialNum");
                invoiceSettingDTO.setInvoiceId(invoiceSerialNum);
                System.out.println(json+"========><");
                //查询发票开具状态
                invoiceSettingDTO.setInvoiceRed("1");//蓝票
                getInfoCX(invoiceSettingDTO);
                map.put("invoiceId",invoiceSerialNum);
                return map;
            }
        } catch (Exception e){
            log.error("InvoiceSettingServiceImpl.createFP.error==>{}",e.getMessage());
            if(e instanceof XXXException){
                throw new XXXException(ErrorCode.SYSTEM_ERROR.getCode(),e.getMessage());
            }
            throw new XXXException(ErrorCode.SYSTEM_ERROR.getCode(),"系统异常,请稍后再试!");
        }
    }

六、查询开票结果

1.相关请求参数
在这里插入图片描述
2.响应结果示例:

{
    "code": "E0000",
    "describe": "获取成功",
    "result": [
        {
            "serialNo": "19010211130401000006",
            "orderNo": "1001000011161",
            "status": "2",
            "statusMsg": "开票完成(最终状态)",
            "failCause": "",
            "pdfUrl": "https://invtest.jss.com.cn/group1/M00/0D/A4/wKjScVwsK6CAFzLgAABsVO-OKaE630.pdf",
            "pictureUrl": "nnfpkf.jss.com.cn/ArQ6dFE3-9o5x4B",
            "invoiceTime": 1546398919000,
            "invoiceCode": "131880930142",
            "invoiceNo": "18757776",
            "exTaxAmount": "0.38",
            "taxAmount": "0.02",
            "payerName": "个人2",
            "payerTaxNo": "110101TRDX8RQU1",
            "invoiceKind": "电子增值税普通发票",
            "checkCode": "72969719882523170140",
            "invoiceItems": [
                {
                    "itemName": "门票",
                    "itemUnit": "张",
                    "itemPrice": "0.300000000000000000",
                    "itemTaxRate": "0.06",
                    "itemNum": "2.000000000000000000",
                    "itemAmount": "0.60",
                    "itemTaxAmount": "0.03",
                    "itemSpec": "",
                    "itemCode": "3070101000000000000",
                    "isIncludeTax": "true",
                    "invoiceLineProperty": "2",
                    "zeroRateFlag": "",
                    "favouredPolicyName": "",
                    "favouredPolicyFlag": "0"
                },
                ……
            ]
        },
        ……
    ]
}

3.示例代码

public Map<String,Object> getInfo(InvoiceSettingDTO invoiceSettingDTO) {
        try {
            Map<String,Object> map = new HashMap<>();
            String status ="";
            String pdfUrl ="";
            String pictureUrl ="";
            String invoiceCode ="";//发票代码
            String invoiceNo ="";//发票号码
            LambdaQueryWrapper<InvoiceInfo> lq1 = new LambdaQueryWrapper<>();
            lq1.eq(InvoiceInfo::getOrderId,invoiceSettingDTO.getOrderId());
            InvoiceInfo invoiceInfo = invoiceInfoMapper.selectOne(lq1);
            String senid = UUID.randomUUID().toString().replace("-", ""); // 唯一标识,32位随机码,无需修改,保持默认即可
            String method = "nuonuo.ElectronInvoice.queryInvoiceResult"; // API方法名
            NNOpenSDK sdk = NNOpenSDK.getIntance();
            String token = getToken();
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("serialNos",invoiceSettingDTO.getInvoiceId());
            jsonObject.put("isOfferInvoiceDetail","0");
            String content = jsonObject.toJSONString();

            log.info("author in ==>"+content);
            String json = sdk.sendPostSyncRequest(baseURL, senid, aPPKey, aPPSecret, token, taxnum, method, content);
            log.info("author out ==>"+json);
            //获取结果json
            JSONObject result = JSONObject.parseObject(json);

            String code = result.getString("code");
            if (code.equals("E0000")){
                //获取结果集
                List<InvoiceInfoVO> infoVOS = result.getJSONArray("result").toJavaList(InvoiceInfoVO.class);
                invoiceSettingDTO.getInvoiceSerialNum();
                if (infoVOS.size()>=0){
                    status = infoVOS.get(0).getStatus();
                    pdfUrl = infoVOS.get(0).getPdfUrl();
                    pictureUrl = infoVOS.get(0).getPictureUrl();
                    invoiceCode = infoVOS.get(0).getInvoiceCode();
                    invoiceNo = infoVOS.get(0).getInvoiceNo();
                    String failCause = infoVOS.get(0).getFailCause();
                    System.out.println(json+"======>>>");
                    System.out.println(json+"======>>>");
                    System.out.println(json+"======>>>");
                    System.out.println(json+"======>>>");
                    if (ObjectUtil.isNotEmpty(failCause)){
                        throw new XXXException(failCause+",请稍后再试,或致电010-88545091");
                    }
                }else {
                    throw new XXXException("调用查询发票接口失败!");
                }
                map.put("status",status);
                map.put("pdfUrl",pdfUrl);
                map.put("pictureUrl",pictureUrl);
                return map;
            }else {
                String errormsg = result.getString("describe");
                throw new HbsxException(errormsg+",请稍后再试");
            }
        } catch (Exception e){
            log.error("InvoiceSettingServiceImpl.getInfo.error==>{}",e.getMessage());

            if(e instanceof XXXException){
                throw new XXXException(ErrorCode.SYSTEM_ERROR.getCode(),e.getMessage());
            }
            throw new XXXException(ErrorCode.SYSTEM_ERROR.getCode(),"系统异常,请稍后再试!");
        }
    }

4.返回码说明:
在这里插入图片描述

以上就是诺诺发票的自助开票和查询接口的使用,其他冲红等接口与这两个接口调用方式一致,如有侵权,请联系我删除文章,学习之路山高路远,还请各位大佬指正!

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值