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.*
+ "
+%>
+
+
+
+
+
+
+
+ 微信交易查询样例
+
+ ↓ 输入单号可以查看交易记录, 单号可以在微信客户端的 我↘ > 钱包 > ┇↗ > 交易消息 > (其中的某一条) > 支付信息 > 商户单号 找到.
+
+ 当然, 只能查到对应此公众号的交易信息.
+
+
+
+
+
+
+
+
+
+ <%
+ 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.*
+ "
+%>
+
+
+
+
+
+
+
+ 微信退款查询样例
+
+ ↓ 输入单号可以查看退款记录, 单号可以在微信客户端的 我↘ > 钱包 > ┇↗ > 交易消息 > (其中的某一条) > 支付信息 > 商户单号 找到.
+
+ 当然, 只能查到对应此公众号的交易信息.
+
+
+
+
+
+
+
+
+
+ <%
+ 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 的交易都可以通过此页面无条件退款, 此接口等同于一个公开的后门. 请勿在生产环境保留此页面.
+
+
+
+
+
+
+
+
+
+ <%
+ 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)%>
+ <%
+ }
+ }
+ %>
+
+
+
+
+