一、准备工作
- 引入xfire
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-core</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-aegis</artifactId>
<version>1.2</version>
</dependency>
- 引入Freemarker模板
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
二、编写业务代码
- 编写获取模板内容工具类
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.springframework.util.ResourceUtils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class FreemarkerUtils {
/**
* 读取freemarkers模板
* @param path 模板所在目录(template/模板名称.html)
* @param model 模板要跳出的数据
* @return
*/
public static String randeHtml(String path,Map<String, Object> model) {
try {
StringWriter stringWriter = new StringWriter();
try {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
模板所在目录 这里是放在resources/templates目录下
String templatePath = ResourceUtils.getURL("classpath:templates/xml").getPath();
cfg.setLocale(Locale.CHINA);
cfg.setEncoding(Locale.CHINA, "UTF-8");
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
cfg.setClassicCompatible(true);
cfg.setTagSyntax(Configuration.AUTO_DETECT_TAG_SYNTAX);
cfg.setWhitespaceStripping(true);
cfg.setDirectoryForTemplateLoading(new File(templatePath));
Template temp = cfg.getTemplate(path);
temp.process(model, stringWriter);
stringWriter.flush();
return stringWriter.toString();
} catch (Exception e) {
log.error(e.getMessage());
try {
stringWriter.close();
} catch (IOException ex) {
log.error(e.getMessage());
}
}
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
}
此工具类用来获取模板html文件报文
- 创建模板文件(报文)
<?xml version='1.0' encoding='UTF-8'?>
<interface version='DZFP1.0'>
<globalInfo>
<terminalCode>${terminalCode}</terminalCode>
<appId>${appId}</appId>
<version>${version}</version>
<interfaceCode>${interfaceCode}</interfaceCode>
<userName>${userName}</userName>
<passWord>${passWord}</passWord>
<taxpayerId>${taxpayerId}</taxpayerId>
<authorizationCode>${authorizationCode}</authorizationCode>
<requestCode>${requestCode}</requestCode>
<requestTime>${requestTime}</requestTime>
<responseCode/>
<dataExchangeId>${dataExchangeId}</dataExchangeId>
</globalInfo>
<returnStateInfo>
<returnCode/>
<returnMessage/>
</returnStateInfo>
<Data>
<dataDescription>
<zipCode>${zipCode}</zipCode>
<encryptCode>${encryptCode}</encryptCode>
<codeType>${codeType}</codeType>
</dataDescription>
<content>${content}</content>
</Data>
</interface>
<REQUEST_FPKJXX class="REQUEST_FPKJXX">
<FPKJXX_FPTXX class="FPKJXX_FPTXX">
<FPQQLSH>${FPQQLSH}</FPQQLSH>
<DSPTBM>${DSPTBM}</DSPTBM>
<NSRSBH>${NSRSBH}</NSRSBH>
<NSRMC>${NSRMC}</NSRMC>
<NSRDZDAH/>
<SWJG_DM/>
<DKBZ>${DKBZ}</DKBZ>
<SGBZ/>
<PYDM>${PYDM}</PYDM>
<KPXM>${KPXM}</KPXM>
<BMB_BBH>${BMB_BBH}</BMB_BBH>
<XHF_NSRSBH>${XHF_NSRSBH}</XHF_NSRSBH>
<XHFMC>${XHFMC}</XHFMC>
<XHF_DZ>${XHF_DZ}</XHF_DZ>
<XHF_DH>${XHF_DH}</XHF_DH>
<XHF_YHZH>${XHF_YHZH}</XHF_YHZH>
<GHFMC>${GHFMC}</GHFMC>
<GHF_NSRSBH/>
<GHF_SF/>
<GHF_DZ>${GHF_DZ}</GHF_DZ>
<GHF_GDDH>${GHF_GDDH}</GHF_GDDH>
<GHF_SJ>${GHF_SJ}</GHF_SJ>
<GHF_EMAIL/>
<GHFQYLX>${GHFQYLX}</GHFQYLX>
<GHF_YHZH/>
<HY_DM/>
<HY_MC/>
<KPY>${KPY}</KPY>
<SKY>${SKY}</SKY>
<FHR>${FHR}</FHR>
<KPRQ>${KPRQ}</KPRQ>
<KPLX>${KPLX}</KPLX>
<YFP_DM/>
<YFP_HM/>
<CZDM>${CZDM}</CZDM>
<QD_BZ>${QD_BZ}</QD_BZ>
<QDXMMC/>
<CHYY/>
<TSCHBZ>${TSCHBZ}</TSCHBZ>
<KPHJJE>${KPHJJE}</KPHJJE>
<HJBHSJE>${HJBHSJE}</HJBHSJE>
<HJSE>${HJSE}</HJSE>
<BZ>${BZ}</BZ>
<BYZD1/>
<BYZD2/>
<BYZD3/>
<BYZD4/>
<BYZD5/>
</FPKJXX_FPTXX>
<FPKJXX_XMXXS class="FPKJXX_XMXX;" size="${itemSize}">
<#list items as item>
<FPKJXX_XMXX>
<XMMC>${item.XMMC}</XMMC>
<XMDW>${item.XMDW}</XMDW>
<GGXH>${item.GGXH}</GGXH>
<XMSL>${item.XMSL}</XMSL>
<HSBZ>${item.HSBZ}</HSBZ>
<XMDJ>${item.XMDJ}</XMDJ>
<FPHXZ>${item.FPHXZ}</FPHXZ>
<SPBM>${item.SPBM}</SPBM>
<ZXBM/>
<YHZCBS>${item.YHZCBS}</YHZCBS>
<LSLBS/>
<ZZSTSGL/>
<KCE/>
<XMJE>${item.XMJE}</XMJE>
<SL>${item.SL}</SL>
<SE>${item.SE}</SE>
<BYZD1/>
<BYZD2/>
<BYZD3/>
<BYZD4/>
<BYZD5/>
</FPKJXX_XMXX>
</#list>
</FPKJXX_XMXXS>
<FPKJXX_DDXX class="FPKJXX_DDXX">
<DDH/>
<THDH/>
<DDDATE/>
</FPKJXX_DDXX>
<FPKJXX_DDMXXXS class="FPKJXX_DDMXXX;" size="1">
<FPKJXX_DDMXXX>
<DDMC/>
<DW/>
<GGXH/>
<SL/>
<DJ/>
<JE/>
<BYZD1/>
<BYZD2/>
<BYZD3/>
<BYZD4/>
<BYZD5/>
</FPKJXX_DDMXXX>
</FPKJXX_DDMXXXS>
<FPKJXX_ZFXX class="FPKJXX_ZFXX">
<ZFFS/>
<ZFLSH/>
<ZFPT/>
</FPKJXX_ZFXX>
<FPKJXX_WLXX class="FPKJXX_WLXX">
<CYGS/>
<SHSJ/>
<WLDH/>
<SHDZ/>
</FPKJXX_WLXX>
</REQUEST_FPKJXX>
-
编写工具类AisinoUtil
import java.beans.Encoder;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
import com.zhixuan.common.utils.DateUtils;
import com.zhixuan.common.utils.SerialNumberUtils;
import com.zhixuan.modules.vat.config.AisinoConfig;
import com.zhixuan.modules.vat.dto.RecordsDTO;
public class AisinoUtils {
/**
* 生成PassWord
* @return
*/
public static String generatePassWord(){
String passWord = null;
String s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder sBuilder = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 10; i++) {
int ran = random.nextInt(s.length());
sBuilder.append(s.charAt(ran));
}
String rString = sBuilder.toString();
sBuilder.append(AisinoConfig.REGISTER_CODE);
final String md5String = getMD5String(sBuilder.toString());
final Base64.Encoder encoder = Base64.getEncoder();
try {
passWord = rString+encoder.encodeToString(md5String.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return passWord;
}
/**
* 航天开具发票(异步)
* 内层报文
* @param dto
* @return
*/
public static String makeContent(RecordsDTO dto) {
Map<String, Object> model = new HashMap<String, Object>();
model.put("FPQQLSH", dto.getSerialNumber());//发票请求唯一流水号
model.put("DSPTBM", AisinoConfig.TERRACE_CODE);//平台编码
model.put("NSRSBH", dto.getMerchant().getTaxNumber());//开票方识别号
model.put("NSRMC", dto.getMerchant().getName());//开票方名称
model.put("NSRDZDAH", "");//开票方电子档案号
model.put("SWJG_DM", "");//税务机构代码
model.put("DKBZ", "0");//代开标志
// model.put("SGBZ", "");//收购标志
model.put("PYDM", AisinoConfig.INVOICE_CODE);//票样代码
model.put("KPXM", dto.getRecordsList().get(0).getCommodityName());//主要开票项目
model.put("BMB_BBH", AisinoConfig.CODE_VERSION);//编码表版本号
model.put("XHF_NSRSBH", AisinoConfig.TAXPAYER_REGISTRATION_CODE);//销货方识别号
model.put("XHFMC", dto.getMerchant().getName());//销货方名称
model.put("XHF_DZ", dto.getMerchant().getAddress());//销货方地址
model.put("XHF_DH", dto.getMerchant().getTel());//销货方电话
model.put("XHF_YHZH", dto.getMerchant().getBankName()+dto.getMerchant().getBankAccount());//销货方银行账号
model.put("GHFMC", dto.getClienteleName());//购货方名称
// model.put("GHF_NSRSBH", "");//购货方识别号
// model.put("GHF_SF", "");//购货方省份
model.put("GHF_DZ", dto.getClienteleAddress());//购货方地址
model.put("GHF_GDDH", dto.getClienteleTel());//购货方固定电话
model.put("GHF_SJ", "15000000000");//购货方手机
// model.put("GHF_EMAIL", "");//购货方邮箱
model.put("GHFQYLX", "01");//购货方企业类型 01:企业 02:机关事业单位 03:个人 04:其它
// model.put("GHF_YHZH", "");//购货方银行账号
// model.put("HY_DM", "");//行业代码
// model.put("HY_MC", "");//行业名称
model.put("KPY", dto.getPayee());//开票员
model.put("SKY", dto.getReview());//收款员
model.put("FHR", dto.getDrawer());//复核人
model.put("KPRQ", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));//开票日期
model.put("KPLX", "1");//开票类型 1 正票 2 红票
// model.put("YFP_DM", "");//原发票代码
// model.put("YFP_HM", "");//原发票号码
model.put("CZDM", "10");//操作代码 10 正票正常开具、 11 正票错票重开、 20 退货折让红票、 21 错票重开红票、
model.put("QD_BZ", "0");//清单标志 0 正常冲红或正常 蓝票开具(电子发 35. TSCHBZ 特殊冲红标志 CHAR 1 是 票) 1 特殊冲红(冲红纸 质等)
// model.put("QDXMMC", "");//清单发票项目名称 清单标识(QD_BZ) 为 1 时必填000000
// model.put("CHYY", "");//冲红原因
model.put("TSCHBZ", "0");//特殊冲红标志
model.put("KPHJJE", dto.getCommodityAmount());//价税合计金额
model.put("HJBHSJE", dto.getAmount());//合计不含税金额
model.put("HJSE", dto.getTaxAmount());//合计税额
model.put("BZ", ".");//备注
model.put("itemSize", dto.getRecordsList().size());//子项目长度
List<Map<String, String>> items = new ArrayList<>();
dto.getRecordsList().forEach(i -> {
Map<String, String> item = new HashMap<String, String>();
item.put("XMMC", i.getCommodityName());//项目名称
item.put("XMDW", i.getUnitMeasurement());//项目单位
item.put("GGXH", i.getSpecifications());//规格型号
item.put("XMSL", i.getNumber().intValue()+"");//项目数量
item.put("HSBZ", i.getTaxFlag() ? "1":"0");//含税标志
System.out.println(i.getUnitPrice());
item.put("XMDJ", i.getUnitPrice().toString());//项目单价
item.put("FPHXZ", "0");//发票行性质 0 正常行、 1 折扣行、 2 被折扣行
item.put("SPBM", "1100301010000000000");//商品编码
item.put("YHZCBS", i.getPreferentialPolicyFlag() ? "1":"0");//优惠政策标识
// item.put("LSLBS", "0");//零税率标识
// item.put("ZZSTSGL", "出口零税 ");//增值税特殊管理
// item.put("KCE", "");//扣除额
item.put("XMJE", i.getCommodityAmount().toString());//项目金额
item.put("SL", i.getTaxRate().toString());//税率
item.put("SE", i.getTaxAmount().toString());//税额
items.add(item);
});
model.put("items", items);//明细集合
// model.put("DDH", "");//订单号
// model.put("THDH", "");//退货单号
// model.put("DDDATE", "");//订单时间
// model.put("DDMC", "1");//订单名称
// model.put("DW", "1");//单位
// model.put("GGXH", "1");//规格型号
// model.put("SL", "1");//数量
// model.put("DJ", "1");//单价
// model.put("JE", "1");//金额end
// model.put("ZFFS", "1");//支付方式
// model.put("ZFLSH", "1");//支付流水号
// model.put("ZFPT", "1");//支付平台end
// model.put("CYGS", "1");//承运公司
// model.put("SHSJ", "1");//送货时间
// model.put("WLDH", "1");//物流单号
// model.put("SHDZ", "1");//送货地址
return formatBase64(FreemarkerUtils.randeHtml("make.html", model));
}
/**
* 航天接口外层报文
* @param dto
* @param content
* @return
*/
public static String outer(RecordsDTO dto,String content) {
Map<String, Object> model = new HashMap<String, Object>();
model.put("terminalCode", AisinoConfig.BS);//终端类型标识
model.put("appId", AisinoConfig.ELECTRONIC_INVOICE);//固定为:DZFP 表示普通发票。ZZS_PT_DZFP 表示增值税普通电子发票
model.put("version", AisinoConfig.INTERFACE_VERSION);//接口版本号
model.put("interfaceCode", AisinoConfig.MAKE);//接口编码
model.put("userName", dto.getClienteleName());//用户编码
model.put("passWord", AisinoUtils.generatePassWord());//
model.put("taxpayerId", AisinoConfig.TAXPAYER_REGISTRATION_CODE);//纳税人识别号
model.put("authorizationCode", AisinoConfig.AUTHORIZTION_CODE);//接入系统平台授权码(由服务商提供)
model.put("requestCode", dto.getClienteleName());//数据交换请求发出方代码
model.put("requestTime", DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss SS"));//数据交换请求发出时间
model.put("dataExchangeId", SerialNumberUtils.generateDataSerialNumber());//数据交换流水号
model.put("zipCode", AisinoConfig.ZERO);// (0,1根据数据包大小判断是否进行压缩)
model.put("encryptCode", AisinoConfig.ZERO);//(加密方式代码)
model.put("codeType", AisinoConfig.BASE64);//(加密方式代码,正式使用时通讯用CA)
model.put("content", content);//base64请求数据内容或返回数据内容
return FreemarkerUtils.randeHtml("outer.html", model);
}
/**
* 航天开具发票(异步)
* @param dto
* @return
*/
public static String invoiceMake(RecordsDTO dto) {
return outer(dto, AisinoUtils.makeContent(dto));
}
/**
* MD5加密
* @param str
* @return
*/
public static String getMD5String(String str) {
try {
// 生成一个MD5加密计算摘要
MessageDigest md = MessageDigest.getInstance("MD5");
// 计算md5函数
md.update(str.getBytes());
// digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
//一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方)
return new BigInteger(1, md.digest()).toString(16);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* base64加密
* @param text
* @return
*/
public static String formatBase64(String text) {
Base64.Encoder encoder = Base64.getEncoder();
try {
return encoder.encodeToString(text.getBytes("UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
return text;
}
/**
* base64解密
* @param text
* @return
*/
public static String parseBase64(String text) {
Base64.Decoder decoder = Base64.getDecoder();
try {
return new String(decoder.decode(text),"UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return text;
}
}
- 发送报文
@Override
public InvoiceResponseDTO invoiceMake(RecordsDTO dto) {
String xml = AisinoUtils.invoiceMake(dto);
try {
Client client = new Client(new URL("接口uri"));
Object[] resultObjects = client.invoke("接口代码", new Object[]{xml});
System.out.println(resultObjects[0].toString());
} catch (Exception e) {
e.printStackTrace();
}
return responseDTO;
}
注意:写报文不要放过任何细节,一个空格都可能解析失败。