diff --git a/doc/web.xml b/doc/web.xml index 43c7edb..c834dd3 100644 --- a/doc/web.xml +++ b/doc/web.xml @@ -27,15 +27,6 @@ /notify.api - - GetOpenId - com.github.cuter44.wxpay.servlet.GetOpenId - - - GetOpenId - /get-openid.api - - JSAPISigner com.github.cuter44.wxpay.servlet.JSAPISigner @@ -47,7 +38,7 @@ SnsapiBase - com.github.cuter44.wxpay.servlet.SnsapiBase + com.github.cuter44.wxmp.servlet.SnsapiBase SnsapiBase @@ -56,7 +47,7 @@ SnsapiUserinfo - com.github.cuter44.wxpay.servlet.SnsapiUserinfo + com.github.cuter44.wxmp.servlet.SnsapiUserinfo SnsapiUserinfo @@ -65,7 +56,7 @@ JSSDKConfig - com.github.cuter44.wxpay.servlet.JSSDKConfig + com.github.cuter44.wxmp.servlet.JSSDKConfig JSSDKConfig diff --git a/src/com/github/cuter44/wxmp/reqs/WxmpRequestBase.java b/src/com/github/cuter44/wxmp/reqs/WxmpRequestBase.java index cea0f2c..96e2cbb 100644 --- a/src/com/github/cuter44/wxmp/reqs/WxmpRequestBase.java +++ b/src/com/github/cuter44/wxmp/reqs/WxmpRequestBase.java @@ -48,7 +48,8 @@ protected static CloseableHttpClient buildHttpClient(SSLContext ctx) { HttpClientBuilder hcb = HttpClientBuilder.create() .disableAuthCaching() - .disableCookieManagement(); + .disableCookieManagement() + .setSslcontext(ctx); return(hcb.build()); } diff --git a/src/com/github/cuter44/wxmp/util/CertificateLoader.java b/src/com/github/cuter44/wxmp/util/CertificateLoader.java index 28dbb62..7fa6156 100644 --- a/src/com/github/cuter44/wxmp/util/CertificateLoader.java +++ b/src/com/github/cuter44/wxmp/util/CertificateLoader.java @@ -8,6 +8,7 @@ import java.security.cert.*; import javax.net.ssl.*; import java.io.*; +import java.util.MissingResourceException; /** Transient tool-object to load certificate and generate ssl-context * Code adapted from nyafx/crypto. @@ -100,8 +101,10 @@ public CertificateLoader loadTrusts(String ... resourcePaths) while (buffer.available() > 0) { Certificate cert = crtFactory.generateCertificate(buffer); - System.out.println(cert); this.trusts.setCertificateEntry("cert-"+System.currentTimeMillis(), cert); + + System.out.println("Trusts:"); + System.out.println(cert); } buffer.close(); @@ -126,13 +129,26 @@ public CertificateLoader loadIdentification(String resourcePath, String passphra InputStream stream = this.getClass() .getResourceAsStream(resourcePath); + if (stream == null) + throw(new MissingResourceException("Load identification failed.", CertificateLoader.class.getName(), resourcePath)); + this.identification = KeyStore.getInstance("PKCS12"); this.identification.load(stream, passphrase.toCharArray()); this.passphrase = passphrase; + + //System.out.println("Identification:"); + //Enumeration aliases = this.identification.aliases(); + //while (aliases.hasMoreElements()) + //{ + //String alias = aliases.nextElement(); + //System.out.println(alias); + //System.out.println(this.identification.getCertificate(alias)); + //} } catch (Exception ex) { + //ex.printStackTrace(); throw(new RuntimeException(ex)); } diff --git a/src/com/github/cuter44/wxpay/constants/RefundStatus.java b/src/com/github/cuter44/wxpay/constants/RefundStatus.java new file mode 100644 index 0000000..21961de --- /dev/null +++ b/src/com/github/cuter44/wxpay/constants/RefundStatus.java @@ -0,0 +1,34 @@ +package com.github.cuter44.wxpay.constants; + +/** RefundState + * @since 0.4.1 + * + */ +public enum RefundStatus +{ + /** code=0, NOTSURE—未确定,需要商户原退款单号重新发起 + */ + NOTSURE(0), + /** code=1, PROCESSING—退款处理中 + */ + PROCESSING(1), + + /** code=16, SUCCES—退款成功 + */ + SUCCESS(16), + + /** code=-1, FAIL—退款失败 + */ + FAIL(-1), + + /** code=-2, CHANGE—转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者财付通转账的方式进行退款。 + */ + CHANGE(-2); + + public byte code; + + private RefundStatus(int code) + { + this.code = (byte)code; + } +} diff --git a/src/com/github/cuter44/wxpay/constants/TradeState.java b/src/com/github/cuter44/wxpay/constants/TradeState.java new file mode 100644 index 0000000..099d4bb --- /dev/null +++ b/src/com/github/cuter44/wxpay/constants/TradeState.java @@ -0,0 +1,51 @@ +package com.github.cuter44.wxpay.constants; + +/** TradeState + * @since 0.4.1 + * + * Notice that code is NOT officially defined by wx. + * And since wx does not provide a status diagram, this distribution is a temporary solution. + * It may be changed in later version. Take consideration before using them. + */ +public enum TradeState +{ + /** code=0, NOTPAY—未支付 + */ + NOTPAY(0), + /** code=1, NOPAY--未支付(输入密码或确认支付超时) + */ + NOPAY(1), + + /** code=2, USERPAYING--用户支付中 + */ + USERPAYING(2), + + /** code=16, SUCCESS—支付成功 + */ + SUCCESS(16), + + /** code=32, REFUND—转入退款 + */ + REFUND(32), + + /** code=64, REVOKED—已撤销 + * This is an unclear state. + */ + REVOKED(64), + + /** code=-1, PAYERROR--支付失败(其他原因,如银行返回失败) + */ + PAYERROR(-1), + + /** code=-128, CLOSED—已关闭 + */ + CLOSED(-128); + + + public byte code; + + private TradeState(int code) + { + this.code = (byte)code; + } +} diff --git a/src/com/github/cuter44/wxpay/constants/WxpayErrorCode.java b/src/com/github/cuter44/wxpay/constants/WxpayErrorCode.java index 56ccbb0..75884e4 100644 --- a/src/com/github/cuter44/wxpay/constants/WxpayErrorCode.java +++ b/src/com/github/cuter44/wxpay/constants/WxpayErrorCode.java @@ -48,7 +48,7 @@ public static WxpayErrorCode forCode(int code) public static WxpayErrorCode forName(String name) { return( - Enum.valueOf(WxpayErrorCode.class, name) + WxpayErrorCode.valueOf(name) ); } diff --git a/src/com/github/cuter44/wxpay/reqs/OrderQuery.java b/src/com/github/cuter44/wxpay/reqs/OrderQuery.java new file mode 100644 index 0000000..b197b6a --- /dev/null +++ b/src/com/github/cuter44/wxpay/reqs/OrderQuery.java @@ -0,0 +1,95 @@ +package com.github.cuter44.wxpay.reqs; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import com.github.cuter44.wxpay.resps.OrderQueryResponse; +import com.github.cuter44.wxpay.WxpayException; +import com.github.cuter44.wxpay.WxpayProtocolException; + +/** + * Created by Mklaus on 15/4/21. + */ +public class OrderQuery extends WxpayRequestBase{ + + public static final String URL_API_BASE = "https://api.mch.weixin.qq.com/pay/orderquery"; + + protected static final String KEY_OUT_TRADE_NO = "out_trade_no"; + protected static final String KEY_TRANSACTION_ID = "transaction_id"; + + public static final List KEYS_PARAM_NAME = Arrays.asList( + "appid", + "mch_id", + "nonce_str", + "out_trade_no", + "transaction_id", + "sign" + ); + + //CONSTRUCT + public OrderQuery(Properties prop){ + super(prop); + + return; + } + + //BUILD + @Override + public OrderQuery build() + { + return(this); + } + + //SIGN + @Override + public OrderQuery sign() + throws UnsupportedEncodingException + { + this.sign(KEYS_PARAM_NAME); + + return(this); + } + + // TO_URL + public String toURL() + throws UnsupportedOperationException + { + throw( + new UnsupportedOperationException("This request does not execute on client side") + ); + } + + // EXECUTE + @Override + public OrderQueryResponse execute() + throws WxpayException, WxpayProtocolException, IOException + { + String url = URL_API_BASE; + String body = this.toXml(KEYS_PARAM_NAME); + + String respXml = this.executePostXML(url, body); + + return(new OrderQueryResponse(respXml)); + } + + /** 商户系统内部的订单号,32个字符内、可包含字母 + */ + public OrderQuery setOutTradeNo(String outTradeNo) + { + this.setProperty(KEY_OUT_TRADE_NO, outTradeNo); + + return(this); + } + + /** 微信的订单号,优先使用 + */ + public OrderQuery setTransactionId(String transactionId) + { + this.setProperty(KEY_TRANSACTION_ID,transactionId); + + return (this); + } +} diff --git a/src/com/github/cuter44/wxpay/reqs/Refund.java b/src/com/github/cuter44/wxpay/reqs/Refund.java new file mode 100644 index 0000000..a3aed54 --- /dev/null +++ b/src/com/github/cuter44/wxpay/reqs/Refund.java @@ -0,0 +1,206 @@ +package com.github.cuter44.wxpay.reqs; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import com.github.cuter44.wxpay.resps.RefundResponse; +import com.github.cuter44.wxpay.resps.OrderQueryResponse; +import com.github.cuter44.wxpay.WxpayException; +import com.github.cuter44.wxpay.WxpayProtocolException; +/** + * Created by Mklaus on 15/4/23. + */ +public class Refund extends WxpayRequestBase { + + public static final String URL_API_BASE = "https://api.mch.weixin.qq.com/secapi/pay/refund"; + + protected static final String KEY_MCH_ID = "mch_id"; + + protected static final String KEY_OUT_TRADE_NO = "out_trade_no"; + protected static final String KEY_OUT_REFUND_NO = "out_refund_no"; + protected static final String KEY_TRANSACTION_ID = "transaction_id"; + protected static final String KEY_TOTAL_FEE = "total_fee"; + protected static final String KEY_REFUND_FEE = "refund_fee"; + protected static final String KEY_OP_USER_ID = "op_user_id"; + + public static final List KEYS_PARAM_NAME = Arrays.asList( + "appid", + "device_info", + "mch_id", + "nonce_str", + "op_user_id", + "out_refund_no", + "out_trade_no", + "refund_fee", + "refund_fee_type", + "sign", + "total_fee", + "transaction_id" + ); + + // CONSTRUCT + public Refund(Properties prop){ + super(prop); + + if (this.getProperty(KEY_OP_USER_ID) == null) + this.setProperty(KEY_OP_USER_ID, this.getProperty(KEY_MCH_ID)); + + return; + } + + /** Fully refund a queried order. + *
+ * out_trade_no is read from ${order.out_trade_no} if not provided in prop. + *
+ * out_refund_no is set to ${order.out_trade_no}-refund if not provided in prop. + * Be cautious not to violate unique/length restrictions. + *
+ * transaction_id is read from ${order.transaction_id} if not provided in prop. + *
+ * total_fee is read from ${order.total_fee} if not provided in prop. + *
+ * refund_fee is read from ${order.total_fee} if not provided in prop. + *
+ * Besides those, all other params provided by order are ignored. + */ + public Refund(Properties prop, OrderQueryResponse order) + { + this(prop); + + Properties p2 = order.getProperties(); + + if (this.getProperty(KEY_OUT_TRADE_NO) == null) + this.setProperty(KEY_OUT_TRADE_NO, p2.getProperty(KEY_OUT_TRADE_NO)); + + if (this.getProperty(KEY_OUT_REFUND_NO) == null) + this.setProperty(KEY_OUT_REFUND_NO, p2.getProperty(KEY_OUT_TRADE_NO)+"-refund"); + + if (this.getProperty(KEY_TRANSACTION_ID) == null) + this.setProperty(KEY_TRANSACTION_ID, p2.getProperty(KEY_TRANSACTION_ID)); + + if (this.getProperty(KEY_TOTAL_FEE) == null) + this.setProperty(KEY_TOTAL_FEE, p2.getProperty(KEY_TOTAL_FEE)); + + if (this.getProperty(KEY_REFUND_FEE) == null) + this.setProperty(KEY_REFUND_FEE, p2.getProperty(KEY_TOTAL_FEE)); + + return; + } + + // BUILD + @Override + public Refund build() + { + return (this); + } + + // SIGN + @Override + public Refund sign() throws UnsupportedEncodingException + { + this.sign(KEYS_PARAM_NAME); + return (this); + } + + // TO_URL + public String toURL() + throws UnsupportedOperationException + { + throw( + new UnsupportedOperationException("This request does not execute on client side") + ); + } + + // EXECUTE + @Override + public RefundResponse execute() + throws WxpayException, WxpayProtocolException, IOException + { + String url = URL_API_BASE; + String body = this.toXml(KEYS_PARAM_NAME); + + String respXml = this.executePostXML(url, body); + + return(new RefundResponse(respXml)); + } + + // PROPERTY + /** 商户系统内部的订单号,32个字符内、可包含字母 + */ + public Refund setOutTradeNo(String outTradeNo) + { + this.setProperty(KEY_OUT_TRADE_NO, outTradeNo); + + return(this); + } + + /** 微信的订单号,优先使用 + */ + public Refund setTransactionId(String transactionId) + { + this.setProperty(KEY_TRANSACTION_ID,transactionId); + + return (this); + } + + /** 商户系统内部的退单号,32个字符内、可包含字母 + */ + public Refund setOutRefundNo(String outRefundNo) + { + this.setProperty(KEY_OUT_REFUND_NO,outRefundNo); + + return (this); + } + + /** 订单总金额,单位为分,不能带小数点 + */ + public Refund setTotalFee(int totalFeeInCNYFen) + { + this.setProperty(KEY_TOTAL_FEE, Integer.toString(totalFeeInCNYFen)); + + return(this); + } + + /** wrap method + */ + public Refund setTotalFee(double totalFeeInCNYYuan) + { + this.setTotalFee( + Double.valueOf(totalFeeInCNYYuan * 100.0).intValue() + ); + + return(this); + } + + /** 退款总金额,单位为分,可以做部分退款 + */ + public Refund setRefundFee(int refundFeeInCNYFen) + { + this.setProperty(KEY_REFUND_FEE,Integer.toString(refundFeeInCNYFen)); + + return (this); + } + + /** wrap method + */ + public Refund setRefundFee(double totalFeeInCNYYuan) + { + this.setRefundFee( + Double.valueOf(totalFeeInCNYYuan * 100.0).intValue() + ); + + return(this); + } + + /** 操作员帐号, 默认为商户号 + */ + public Refund setOpUserId(String opUserId) + { + this.setProperty(KEY_OP_USER_ID, opUserId); + + return (this); + } +} diff --git a/src/com/github/cuter44/wxpay/reqs/RefundQuery.java b/src/com/github/cuter44/wxpay/reqs/RefundQuery.java new file mode 100644 index 0000000..86242f8 --- /dev/null +++ b/src/com/github/cuter44/wxpay/reqs/RefundQuery.java @@ -0,0 +1,123 @@ +package com.github.cuter44.wxpay.reqs; + +import com.github.cuter44.wxpay.resps.RefundQueryResponse; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import com.github.cuter44.wxpay.WxpayException; +import com.github.cuter44.wxpay.WxpayProtocolException; + +/** + * Created by Mklaus on 15/4/23. + */ +public class RefundQuery extends WxpayRequestBase { + //KEY + public static final String URL_API_BASE = "https://api.mch.weixin.qq.com/pay/refundquery"; + + protected static final String KEY_OUT_TRADE_NO = "out_trade_no"; + protected static final String KEY_OUT_REFUND_NO = "out_refund_no"; + protected static final String KEY_REFUND_ID = "refund_id"; + protected static final String KEY_TRANSACTION_ID = "transaction_id"; + + public static final List KEYS_PARAM_NAME = Arrays.asList( + "appid", + "device_info", + "mch_id", + "nonce_str", + "out_refund_no", + "out_trade_no", + "refund_id", + "sign", + "transaction_id" + ); + + // CONSTRUCT + public RefundQuery(Properties prop) + { + super(prop); + + return; + } + + // BUILD + @Override + public RefundQuery build() + { + return (this); + } + + // SIGN + @Override + public RefundQuery sign() throws UnsupportedEncodingException + { + this.sign(KEYS_PARAM_NAME); + return (this); + } + + // EXECUTE + @Override + public RefundQueryResponse execute() + throws WxpayException, WxpayProtocolException, IOException + { + String url = URL_API_BASE; + String body = this.toXml(KEYS_PARAM_NAME); + + String respXml = this.executePostXML(url, body); + + return(new RefundQueryResponse(respXml)); + } + + // TO_URL + public String toURL() + throws UnsupportedOperationException + { + throw( + new UnsupportedOperationException("This request does not execute on client side") + ); + } + + // PROPERTY + + /** 商户系统内部的订单号 + */ + public RefundQuery setOutTradeNo(String outTradeNo) + { + this.setProperty(KEY_OUT_TRADE_NO,outTradeNo); + + return (this); + } + + /** 微信订单号 + */ + public RefundQuery setTransactionId(String transactionId) + { + this.setProperty(KEY_TRANSACTION_ID,transactionId); + + return (this); + } + + /** 商户退款单号 + */ + public RefundQuery setOutRefundNo(String outRefundNo) + { + this.setProperty(KEY_OUT_REFUND_NO,outRefundNo); + + return (this); + } + + /** 微信退款单号 + * refund_id、out_refund_no、out_trade_no、transaction_id 四个参数必填一个,如果同事存在优先级为: + * refund_id>out_refund_no>transaction_id>out_trade_no + */ + public RefundQuery setRefundId(String refundId) + { + this.setProperty(KEY_REFUND_ID,refundId); + + return (this); + } + +} diff --git a/src/com/github/cuter44/wxpay/reqs/WxpayRequestBase.java b/src/com/github/cuter44/wxpay/reqs/WxpayRequestBase.java index 80aa8e2..deeee14 100644 --- a/src/com/github/cuter44/wxpay/reqs/WxpayRequestBase.java +++ b/src/com/github/cuter44/wxpay/reqs/WxpayRequestBase.java @@ -59,7 +59,8 @@ protected static CloseableHttpClient buildHttpClient(SSLContext ctx) { HttpClientBuilder hcb = HttpClientBuilder.create() .disableAuthCaching() - .disableCookieManagement(); + .disableCookieManagement() + .setSslcontext(ctx); return(hcb.build()); } @@ -181,25 +182,27 @@ protected String signMD5(List paramNames, String key) return(sign); } - // TO_URL - /** Extract URL to execute request on client - */ - public abstract String toURL() - throws UnsupportedOperationException; - /** Provide query string to sign(). - * toURL() may not invoke this method. + * This method do not parse sign, thus toURL() must not invoke this method. */ protected String toQueryString(List paramNames) { URLBuilder ub = new URLBuilder(); for (String key:paramNames) - ub.appendParam(key, this.getProperty(key)); + if (!KEY_SIGN.equals(key)) + ub.appendParam(key, this.getProperty(key)); return(ub.toString()); } + + // TO_URL + /** Extract URL to execute request on client + */ + public abstract String toURL() + throws UnsupportedOperationException; + // TO_XML protected String toXml(List paramNames) { diff --git a/src/com/github/cuter44/wxpay/resps/Notify.java b/src/com/github/cuter44/wxpay/resps/Notify.java index d5a18f4..2c24d05 100644 --- a/src/com/github/cuter44/wxpay/resps/Notify.java +++ b/src/com/github/cuter44/wxpay/resps/Notify.java @@ -44,6 +44,8 @@ public class Notify extends WxpayResponseBase // CONSTRUCT + /** @deprecated use Notify(String notifyXml) instead. + */ public Notify(WxpayResponseBase resp) { this(resp.respString, resp.respProp); @@ -51,6 +53,8 @@ public Notify(WxpayResponseBase resp) return; } + /** @deprecated use Notify(String notifyXml) instead. + */ public Notify(String respString, Properties respProp) { super(respString, respProp); @@ -58,6 +62,13 @@ public Notify(String respString, Properties respProp) return; } + public Notify(String notifyXml) + { + super(notifyXml); + + return; + } + // VERIFY @Override protected boolean verifySign(Properties conf) diff --git a/src/com/github/cuter44/wxpay/resps/OrderQueryResponse.java b/src/com/github/cuter44/wxpay/resps/OrderQueryResponse.java new file mode 100644 index 0000000..9bfc6e7 --- /dev/null +++ b/src/com/github/cuter44/wxpay/resps/OrderQueryResponse.java @@ -0,0 +1,110 @@ +package com.github.cuter44.wxpay.resps; + +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import com.github.cuter44.wxpay.constants.*; + +/** + * Created by Mklaus on 15/4/21. + */ +public class OrderQueryResponse extends WxpayResponseBase { + + private static final String KEY_OPENID = "openid"; + private static final String KEY_TRANSACTION_ID = "trade_state"; + private static final String KEY_TRADE_STATE = "trade_state"; + private static final String KEY_TOTAL_FEE = "total_fee"; + private static final String KEY_COUPON_FEE = "coupon_fee"; + private static final String KEY_TIME_END = "time_end"; + + public static final List KEYS_PARAM_NAME = Arrays.asList( + "appid", + "attach", + "bank_type", + "coupon_fee", + "device_info", + "fee_type", + "is_subscribe", + "mch_id", + "nonce_str", + "openid", + "out_trade_no", + "time_end", + "total_fee", + "trade_state", + "trade_type", + "transaction_id", + "sign" + ); + + //CONSTRUCT + public OrderQueryResponse(String respXml) + { + super(respXml); + + return; + } + + // VERIFY + @Override + protected boolean verifySign(Properties conf) + throws UnsupportedEncodingException + { + return( + this.verifySign(KEYS_PARAM_NAME, conf) + ); + } + + //PROPERTY + public String getTransactionId() + { + return( + this.getProperty(KEY_TRANSACTION_ID) + ); + } + + public String getOpenid() + { + return( + this.getProperty(KEY_OPENID) + ); + } + + public TradeState getTradeState() + { + return( + TradeState.valueOf( + this.getProperty(KEY_TRADE_STATE) + ) + ); + } + + /** @return total_fee in CNY fen + */ + public int getTotalFee(){ + return( + Integer.valueOf( + this.getProperty(KEY_TOTAL_FEE) + ) + ); + } + + public int getCouponFee(){ + String sCouponFee = this.getProperty(KEY_COUPON_FEE); + + if (sCouponFee!=null) + return( + Integer.valueOf(sCouponFee) + ); + // else + return(0); + } + + public String getTimeEnd(){ + return( + this.getProperty(KEY_TIME_END) + ); + } +} diff --git a/src/com/github/cuter44/wxpay/resps/RefundQueryResponse.java b/src/com/github/cuter44/wxpay/resps/RefundQueryResponse.java new file mode 100644 index 0000000..69629c8 --- /dev/null +++ b/src/com/github/cuter44/wxpay/resps/RefundQueryResponse.java @@ -0,0 +1,229 @@ +package com.github.cuter44.wxpay.resps; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import com.github.cuter44.wxpay.constants.RefundStatus; + +/** + * Created by Mklaus on 15/4/23. + */ +public class RefundQueryResponse extends WxpayResponseBase { + + + public static final String KEY_APPID = "appid"; + public static final String KEY_COUPON_REFUND_FEE_ = "refund_fee_"; + public static final String KEY_DEVICE_INFO = "device_info"; + public static final String KEY_MCH_ID = "mch_id"; + public static final String KEY_NONCE_STR = "nonce_str"; + public static final String KEY_OUT_REFUND_NO_ = "out_refund_no_"; + public static final String KEY_OUT_TRADE_NO = "nonce_str"; + public static final String KEY_REFUND_CHANNEL_ = "refund_channel_"; + public static final String KEY_REFUND_COUNT = "refund_count"; + public static final String KEY_REFUND_FEE_ = "refund_fee_"; + public static final String KEY_REFUND_ID_ = "refund_id_"; + public static final String KEY_REFUND_STATUS_ = "refund_status_"; + public static final String KEY_TOTAL_FEE = "total_fee"; + public static final String KEY_TRANSACTION_ID = "transaction_id"; + public static final String KEY_SIGN = "sign"; + + public List keysParamName = null; + + //public static final List KEYS_PARAM_NAME = Arrays.asList( + //"appid", + //"coupon_refund_fee_0", + //"device_info", + //"mch_id", + //"nonce_str", + //"out_refund_no_0", + //"out_trade_no", + //"refund_channel_0", + //"refund_count", + //"refund_fee_0", + //"refund_id_0", + //"refund_status_0", + //"total_fee", + //"transaction_id", + //"sign" + //); + + //CONSTRUCT + public RefundQueryResponse(String respXml) + { + super(respXml); + + return; + } + + //VERIFY + //public List build_keys_param_name() + //{ + //List params = KEYS_PARAM_NAME; + + //for (int i = 1; i < Integer.valueOf(this.getProperty("refund_count")); i++) + //{ + //params.add((i + 1 ), "coupon_refund_fee_" + i); + //params.add((i * 2 + 5 ), "out_refund_no_" + i); + //params.add((i * 3 + 7 ), "refund_channel_" + i); + //params.add((i * 4 + 9 ), "refund_fee_" + i); + //params.add((i * 5 + 10 ), "refund_id_" + i); + //params.add((i * 6 + 11 ), "refund_status_" + i); + //} + + //return params; + //} + + //@Override + protected boolean verifySign(Properties conf) + throws UnsupportedEncodingException + { + if (this.keysParamName == null) + { + int count = this.getRefundCount(); + List l = new ArrayList(10+6*count); + + keysParamName.add (KEY_APPID ); + for (int i=0; i KEYS_PARAM_NAME = Arrays.asList( + "appid", + "coupon_refund_fee", + "device_info", + "mch_id", + "nonce_str", + "out_refund_no", + "out_trade_no", + "refund_channel", + "refund_fee", + "refund_id", + "transaction_id", + "sign" + ); + + //CONSTRUCT + public RefundResponse(String respXml) + { + super(respXml); + + return; + } + + // VERIFY + @Override + protected boolean verifySign(Properties conf) + throws UnsupportedEncodingException + { + return( + this.verifySign(KEYS_PARAM_NAME, conf) + ); + } + + // GET + public String getProperty(String key) + { + return( + this.respProp.getProperty(key) + ); + } + + // PROPERTY + public String getRefundId() + { + return( + this.getProperty(KEY_REFUND_ID) + ); + } + + /** @return refund_fee in CNY fen + */ + public int getRefundFee(){ + return( + Integer.valueOf( + this.getProperty(KEY_REFUND_FEE) + ) + ); + } + + /** @return coupon_refund_fee in CNY fen + */ + public int getCouponRefundFee(){ + String sCouponRefundFee = this.getProperty(KEY_COUPON_REFUND_FEE); + + if (sCouponRefundFee!=null) + return( + Integer.valueOf(sCouponRefundFee) + ); + // else + return(0); + } +} diff --git a/src/com/github/cuter44/wxpay/resps/WxpayResponseBase.java b/src/com/github/cuter44/wxpay/resps/WxpayResponseBase.java index a68dc60..9b41bac 100644 --- a/src/com/github/cuter44/wxpay/resps/WxpayResponseBase.java +++ b/src/com/github/cuter44/wxpay/resps/WxpayResponseBase.java @@ -21,6 +21,7 @@ public class WxpayResponseBase { // CONSTANTS public static final String KEY_KEY = "key_key"; + public static final String KEY_SIGN = "key_sign"; public static final String KEY_SKIP_VERIFY_SIGN = "SKIP_VERIFY_SIGN"; public static final String KEY_RETURN_CODE = "return_code"; @@ -206,7 +207,8 @@ protected String toQueryString(List paramNames) URLBuilder ub = new URLBuilder(); for (String key:paramNames) - ub.appendParam(key, this.getProperty(key)); + if (!KEY_SIGN.equals(key)) + ub.appendParam(key, this.getProperty(key)); return(ub.toString()); } @@ -226,12 +228,13 @@ protected boolean verifySign(List paramNames, Properties conf) } /** 子类应该实现这个方法以验证签名 - * SUB-CLASS MUST IMPLEMENT THIS METHOD TO BE CALLBACKED. + * SUB-CLASS MUST IMPLEMENT THIS METHOD TO BE CALLBACKED. Default behavior do no verification, i.e. always return true. */ protected boolean verifySign(Properties conf) throws UnsupportedEncodingException, UnsupportedOperationException { - throw(new UnsupportedOperationException("Don't know which params should be signed.")); + // DEFAULT IMPLEMENT + return(true); } /** diff --git a/src/com/github/cuter44/wxpay/servlet/WxpayNotifyGatewayServlet.java b/src/com/github/cuter44/wxpay/servlet/WxpayNotifyGatewayServlet.java index 8d1eda8..e044d1f 100644 --- a/src/com/github/cuter44/wxpay/servlet/WxpayNotifyGatewayServlet.java +++ b/src/com/github/cuter44/wxpay/servlet/WxpayNotifyGatewayServlet.java @@ -34,6 +34,8 @@ public boolean handle(Notify n) @Override public void init(ServletConfig config) { + this.gateway = WxpayNotifyPublisher.getDefaultInstance(); + if (Boolean.valueOf(config.getInitParameter("com.github.cuter44.wxpay.notifygateway.dump"))) { this.gateway.addListener( @@ -69,8 +71,6 @@ public boolean handle(Notify n) } ); } - - this.gateway = WxpayNotifyPublisher.getDefaultInstance(); } @Override diff --git a/web/demo-order-query.jsp b/web/demo-order-query.jsp new file mode 100644 index 0000000..c035bb5 --- /dev/null +++ b/web/demo-order-query.jsp @@ -0,0 +1,61 @@ +<%@ page language="java" pageEncoding="UTF-8" + import=" + java.util.Properties, + com.github.cuter44.wxpay.*, + com.github.cuter44.wxpay.reqs.*, + com.github.cuter44.wxpay.resps.* + " +%> + + + + + + + +

微信交易查询样例

+ + ↓ 输入单号可以查看交易记录, 单号可以在微信客户端的 我↘ > 钱包 > ┇↗ > 交易消息 > (其中的某一条) > 支付信息 > 商户单号 找到. +
+ 当然, 只能查到对应此公众号的交易信息. + +

+ +

+ + + +
out_trade_no
+
+ +

+ +

+ + <% + String outTradeNo = request.getParameter("outTradeNo"); + if (outTradeNo != null) + { + WxpayFactory factory = WxpayFactory.getDefaultInstance(); + + OrderQuery wxreq = new OrderQuery(factory.getConf()); + wxreq.setOutTradeNo(outTradeNo); + + OrderQueryResponse wxresp = wxreq.build().sign().execute(); + Properties prop = wxresp.getProperties(); + System.out.println(prop); + + for (Object k:prop.keySet()) + { + %> +
<%=k%> +
<%=prop.get(k)%> + <% + } + } + %> + +
+ + + diff --git a/web/demo-pay.jsp b/web/demo-pay.jsp index 6e75e75..9c53833 100644 --- a/web/demo-pay.jsp +++ b/web/demo-pay.jsp @@ -34,7 +34,7 @@ if (!getParamValue("openid")) { var thisUrl = location.href; - location.href="http://weixin.uutime.cn/wxpay/get-openid.api?redir="+encodeURIComponent(thisUrl); + location.href="snsapi-base.api?redir="+encodeURIComponent(thisUrl); } else { @@ -47,7 +47,7 @@ var ajax = new XMLHttpRequest(); ajax.open( "POST", - "http://weixin.uutime.cn/wxpay/jsapi-signer.api", + "jsapi-signer.api", false ); ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded"); diff --git a/web/demo-refund-query.jsp b/web/demo-refund-query.jsp new file mode 100644 index 0000000..8877e5c --- /dev/null +++ b/web/demo-refund-query.jsp @@ -0,0 +1,61 @@ +<%@ page language="java" pageEncoding="UTF-8" + import=" + java.util.Properties, + com.github.cuter44.wxpay.*, + com.github.cuter44.wxpay.reqs.*, + com.github.cuter44.wxpay.resps.* + " +%> + + + + + + + +

微信退款查询样例

+ + ↓ 输入单号可以查看退款记录, 单号可以在微信客户端的 我↘ > 钱包 > ┇↗ > 交易消息 > (其中的某一条) > 支付信息 > 商户单号 找到. +
+ 当然, 只能查到对应此公众号的交易信息. + +

+ +

+ + + +
out_trade_no
+
+ +

+ +

+ + <% + String outTradeNo = request.getParameter("outTradeNo"); + if (outTradeNo != null) + { + WxpayFactory factory = WxpayFactory.getDefaultInstance(); + + RefundQuery wxreq = new RefundQuery(factory.getConf()); + wxreq.setOutTradeNo(outTradeNo); + + RefundQueryResponse wxresp = wxreq.build().sign().execute(); + Properties prop = wxresp.getProperties(); + System.out.println(prop); + + for (Object k:prop.keySet()) + { + %> +
<%=k%> +
<%=prop.get(k)%> + <% + } + } + %> + +
+ + + diff --git a/web/demo-refund.jsp b/web/demo-refund.jsp new file mode 100644 index 0000000..8879f00 --- /dev/null +++ b/web/demo-refund.jsp @@ -0,0 +1,71 @@ +<%@ page language="java" pageEncoding="UTF-8" + import=" + java.util.Properties, + com.github.cuter44.wxpay.*, + com.github.cuter44.wxpay.reqs.*, + com.github.cuter44.wxpay.resps.* + " +%> + + + + + + + +

微信交易退款请求样例

+ + ↓ 输入单号可以退款, 单号可以在微信客户端的 我↘ > 钱包 > ┇↗ > 交易消息 > (其中的某一条) > 支付信息 > 商户单号 找到. +
+ 自动退全额. +
+ 警告: 所有关联此 appid 的交易都可以通过此页面无条件退款, 此接口等同于一个公开的后门. 请勿在生产环境保留此页面. + +

+ +

+ + + +
out_trade_no
+
+ +

+ +

+ + <% + String outTradeNo = request.getParameter("outTradeNo"); + if (outTradeNo != null) + { + WxpayFactory factory = WxpayFactory.getDefaultInstance(); + + + OrderQuery wxreq1 = new OrderQuery(factory.getConf()); + wxreq1.setOutTradeNo(outTradeNo); + + OrderQueryResponse wxresp1 = wxreq1.build().sign().execute(); + Properties prop1 = wxresp1.getProperties(); + System.out.println(prop1); + + + Refund wxreq2 = new Refund(factory.getConf(), wxresp1); + + RefundResponse wxresp2 = wxreq2.build().sign().execute(); + Properties prop2 = wxresp2.getProperties(); + System.out.println(prop2); + + for (Object k:prop2.keySet()) + { + %> +
<%=k%> +
<%=prop2.get(k)%> + <% + } + } + %> + +
+ + +