pom.xmlphp
<dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency>
XStreamInitializer:java
package cn.edu360.bike.test; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; import com.thoughtworks.xstream.security.NullPermission; import com.thoughtworks.xstream.security.PrimitiveTypePermission; import java.io.Writer; public class XStreamInitializer { public static XStream getInstance() { XStream xstream = new XStream(new PureJavaReflectionProvider(), new XppDriver() { @Override public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out, getNameCoder()) { protected String PREFIX_CDATA = "<![CDATA["; protected String SUFFIX_CDATA = "]]>"; protected String PREFIX_MEDIA_ID = "<MediaId>"; protected String SUFFIX_MEDIA_ID = "</MediaId>"; @Override protected void writeText(QuickWriter writer, String text) { if (text.startsWith(this.PREFIX_CDATA) && text.endsWith(this.SUFFIX_CDATA)) { writer.write(text); } else if (text.startsWith(this.PREFIX_MEDIA_ID) && text.endsWith(this.SUFFIX_MEDIA_ID)) { writer.write(text); } else { super.writeText(writer, text); } } @Override public String encodeNode(String name) { return name;//防止將_轉換成__ } }; } }); xstream.ignoreUnknownElements(); xstream.setMode(XStream.NO_REFERENCES); xstream.addPermission(NullPermission.NULL); xstream.addPermission(PrimitiveTypePermission.PRIMITIVES); return xstream; } }
BaseWxPayResult:git
package cn.edu360.bike.test; import com.google.common.base.Joiner; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import lombok.*; import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; @Data public abstract class BaseWxPayResult { /** * 返回狀態碼. */ @XStreamAlias("return_code") protected String returnCode; /** * 返回信息. */ @XStreamAlias("return_msg") protected String returnMsg; //當return_code爲SUCCESS的時候,還會包括如下字段: /** * 業務結果. */ @XStreamAlias("result_code") private String resultCode; /** * 錯誤代碼. */ @XStreamAlias("err_code") private String errCode; /** * 錯誤代碼描述. */ @XStreamAlias("err_code_des") private String errCodeDes; /** * 公衆帳號ID. */ @XStreamAlias("appid") private String appid; /** * 商戶號. */ @XStreamAlias("mch_id") private String mchId; /** * 服務商模式下的子公衆帳號ID. */ @XStreamAlias("sub_appid") private String subAppId; /** * 服務商模式下的子商戶號. */ @XStreamAlias("sub_mch_id") private String subMchId; /** * 隨機字符串. */ @XStreamAlias("nonce_str") private String nonceStr; /** * 簽名. */ @XStreamAlias("sign") private String sign; //如下爲輔助屬性 /** * xml字符串. */ private String xmlString; /** * xml的Document對象,用於解析xml文本. */ private Document xmlDoc; /** * 將xml字符串轉換成Document對象,以便讀取其元素值. */ private Document getXmlDoc() { if (this.xmlDoc != null) { return this.xmlDoc; } try { this.xmlDoc = DocumentBuilderFactory .newInstance() .newDocumentBuilder() .parse(new ByteArrayInputStream(this.xmlString.getBytes("UTF-8"))); return xmlDoc; } catch (SAXException | IOException | ParserConfigurationException e) { throw new RuntimeException("非法的xml文本內容:" + this.xmlString); } } /** * 將bean經過保存的xml字符串轉換成map. */ public Map<String, String> toMap() { if (StringUtils.isBlank(this.xmlString)) { throw new RuntimeException("xml數據有問題,請覈實!"); } Map<String, String> result = new HashMap<>(); Document doc = this.getXmlDoc(); try { NodeList list = (NodeList) XPathFactory.newInstance().newXPath() .compile("/xml/*") .evaluate(doc, XPathConstants.NODESET); int len = list.getLength(); for (int i = 0; i < len; i++) { result.put(list.item(i).getNodeName(), list.item(i).getTextContent()); } } catch (XPathExpressionException e) { throw new RuntimeException("非法的xml文本內容:" + xmlString); } return result; } /** * 從xml字符串建立bean對象. */ public static <T extends BaseWxPayResult> T fromXML(String xmlString, Class<T> clz) { XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(clz); T result = (T) xstream.fromXML(xmlString); result.setXmlString(xmlString); return result; } /** * 獲取xml中元素的值. */ public String getXmlValue(String... path) { Document doc = this.getXmlDoc(); String expression = String.format("/%s//text()", Joiner.on("/").join(path)); try { return (String) XPathFactory .newInstance() .newXPath() .compile(expression) .evaluate(doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new RuntimeException("未找到相應路徑的文本:" + expression); } } /** * 獲取xml中元素的值,做爲int值返回. */ Integer getXmlValueAsInt(String... path) { String result = this.getXmlValue(path); if (StringUtils.isBlank(result)) { return null; } return Integer.valueOf(result); } }
WxPayOrderQueryResult:spring
package cn.edu360.bike.test; import com.google.common.collect.Lists; import com.thoughtworks.xstream.annotations.XStreamAlias; import lombok.*; import java.util.List; /** * <pre> * 查詢訂單 返回結果對象 * Created by Binary Wang on 2016-10-24. * 註釋中各行每一個字段描述對應以下: * <li>字段名 * <li>變量名 * <li>是否必填 * <li>類型 * <li>示例值 * <li>描述 * </pre> * */ @Data @NoArgsConstructor @XStreamAlias("xml") public class WxPayOrderQueryResult extends BaseWxPayResult { /** * <pre> * 字段名:營銷詳情. * 變量名:promotion_detail * 是否必填:否,單品優惠纔有 * 類型:String(6000) * 示例值:[{"promotion_detail":[{"promotion_id":"109519","name":"單品惠-6","scope":"SINGLE","type":"DISCOUNT","amount":5,"activity_id":"931386","wxpay_contribute":0,"merchant_contribute":0,"other_contribute":5,"goods_detail":[{"goods_id":"a_goods1","goods_remark":"商品備註","quantity":7,"price":1,"discount_amount":4},{"goods_id":"a_goods2","goods_remark":"商品備註","quantity":1,"price":2,"discount_amount":1}]}]} * 描述:單品優惠專用參數,詳見https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_201&index=3 * </pre> */ @XStreamAlias("promotion_detail") private String promotionDetail; /** * <pre>設備號 * device_info * 否 * String(32) * 013467007045764 * 微信支付分配的終端設備號, * </pre> */ @XStreamAlias("device_info") private String deviceInfo; /** * <pre>用戶標識 * openid * 是 * String(128) * oUpF8uMuAJO_M2pxb1Q9zNjWeS6o * 用戶在商戶appid下的惟一標識 * </pre> */ @XStreamAlias("openid") private String openid; /** * <pre>是否關注公衆帳號 * is_subscribe * 否 * String(1) * Y * 用戶是否關注公衆帳號,Y-關注,N-未關注,僅在公衆帳號類型支付有效 * </pre> */ @XStreamAlias("is_subscribe") private String isSubscribe; /** * <pre>交易類型 * trade_type * 是 * String(16) * JSAPI * 調用接口提交的交易類型,取值以下:JSAPI,NATIVE,APP,MICROPAY,詳細說明見參數規定 * </pre> */ @XStreamAlias("trade_type") private String tradeType; /** * <pre>交易狀態 * trade_state * 是 * String(32) * SUCCESS * SUCCESS—支付成功,REFUND—轉入退款,NOTPAY—未支付,CLOSED—已關閉,REVOKED—已撤銷(刷卡支付),USERPAYING--用戶支付中,PAYERROR--支付失敗(其餘緣由,如銀行返回失敗) * </pre> */ @XStreamAlias("trade_state") private String tradeState; /** * <pre>付款銀行 * bank_type * 是 * String(16) * CMC * 銀行類型,採用字符串類型的銀行標識 * </pre> */ @XStreamAlias("bank_type") private String bankType; /** * <pre>訂單金額 * total_fee * 是 * Int * 100 * 訂單總金額,單位爲分 * </pre> */ @XStreamAlias("total_fee") private Integer totalFee; /** * <pre>應結訂單金額 * settlement_total_fee * 否 * Int * 100 * 應結訂單金額=訂單金額-非充值代金券金額,應結訂單金額<=訂單金額。 * </pre> */ @XStreamAlias("settlement_total_fee") private Integer settlementTotalFee; /** * <pre>貨幣種類 * fee_type * 否 * String(8) * CNY * 貨幣類型,符合ISO 4217標準的三位字母代碼,默認人民幣:CNY,其餘值列表詳見貨幣類型 * </pre> */ @XStreamAlias("fee_type") private String feeType; /** * <pre>現金支付金額 * cash_fee * 是 * Int * 100 * 現金支付金額訂單現金支付金額,詳見支付金額 * </pre> */ @XStreamAlias("cash_fee") private Integer cashFee; /** * <pre>現金支付貨幣類型 * cash_fee_type * 否 * String(16) * CNY * 貨幣類型,符合ISO 4217標準的三位字母代碼,默認人民幣:CNY,其餘值列表詳見貨幣類型 * </pre> */ @XStreamAlias("cash_fee_type") private String cashFeeType; /** * <pre>代金券金額 * coupon_fee * 否 * Int * 100 * 「代金券」金額<=訂單金額,訂單金額-「代金券」金額=現金支付金額,詳見支付金額 * </pre> */ @XStreamAlias("coupon_fee") private Integer couponFee; /** * <pre>代金券使用數量 * coupon_count * 否 * Int * 1 * 代金券使用數量 * </pre> */ @XStreamAlias("coupon_count") private Integer couponCount; private List<Coupon> coupons; /** * <pre>微信支付訂單號 * transaction_id * 是 * String(32) * 1009660380201506130728806387 * 微信支付訂單號 * </pre> */ @XStreamAlias("transaction_id") private String transactionId; /** * <pre>商戶訂單號 * out_trade_no * 是 * String(32) * 20150806125346 * 商戶系統的訂單號,與請求一致。 * </pre> */ @XStreamAlias("out_trade_no") private String outTradeNo; /** * <pre>附加數據 * attach * 否 * String(128) * 深圳分店 * 附加數據,原樣返回 * </pre> */ @XStreamAlias("attach") private String attach; /** * <pre>支付完成時間 * time_end * 是 * String(14) * 20141030133525 * 訂單支付時間,格式爲yyyyMMddHHmmss,如2009年12月25日9點10分10秒錶示爲20091225091010。其餘詳見時間規則 * </pre> */ @XStreamAlias("time_end") private String timeEnd; /** * <pre>交易狀態描述 * trade_state_desc * 是 * String(256) * 支付失敗,請從新下單支付 * 對當前查詢訂單狀態的描述和下一步操做的指引 * </pre> */ @XStreamAlias("trade_state_desc") private String tradeStateDesc; /** * 經過xml組裝coupons屬性內容 */ public void composeCoupons() { if (this.couponCount != null && this.couponCount > 0) { this.coupons = Lists.newArrayList(); for (int i = 0; i < this.couponCount; i++) { this.coupons.add(new Coupon(this.getXmlValue("xml/coupon_type_" + i), this.getXmlValue("xml/coupon_id_" + i), this.getXmlValueAsInt("xml/coupon_fee_" + i))); } } } @Data @Builder(builderMethodName = "newBuilder") @AllArgsConstructor public static class Coupon { /** * <pre>代金券類型 * coupon_type_$n * 否 * String * CASH * <li>CASH--充值代金券 * <li>NO_CASH---非充值代金券 * 訂單使用代金券時有返回(取值:CASH、NO_CASH)。$n爲下標,從0開始編號,舉例:coupon_type_$0 * </pre> */ private String couponType; /** * <pre>代金券ID * coupon_id_$n * 否 * String(20) * 10000 * 代金券ID, $n爲下標,從0開始編號 * </pre> */ private String couponId; /** * <pre>單個代金券支付金額 * coupon_fee_$n * 否 * Int * 100 * 單個代金券支付金額, $n爲下標,從0開始編號 * </pre> */ private Integer couponFee; } }
test:express
package cn.edu360.bike.test; public class XmlTest { public static void main(String[] args){ // String xml = "<xml><return_code>200</return_code><return_msg>SUCCESS</return_msg><total>123</total></xml>"; // BaseWxPayResult baseWxPayResult = BaseWxPayResult.fromXML(xml, BaseWxPayResult.class); // System.out.println(baseWxPayResult); // System.out.println(baseWxPayResult.toMap()); // System.out.println(baseWxPayResult.getXmlValue("xml/return_msg")); // System.out.println(baseWxPayResult.getXmlValueAsInt("xml/total")); String xml2 = "<xml>" + "<cash_fee>200</cash_fee><coupon_count>1</coupon_count>" + "<transaction_id>123123123</transaction_id>" + "<coupon_type_0>type0</coupon_type_0>" + "<coupon_id_0>0</coupon_id_0>" + "<coupon_fee_0>0</coupon_fee_0></xml>"; WxPayOrderQueryResult queryResult = WxPayOrderQueryResult.fromXML(xml2, WxPayOrderQueryResult.class); queryResult.composeCoupons(); System.out.println(queryResult); } }
參考:
https://gitee.com/52itstyle/spring-boot-pay/tree/master/src/main/java/com/itstyle/modules/weixinpay/controllerapache