申請及配置在這裏:https://blog.csdn.net/dmw412724/article/details/82735906
目錄
一.後臺統一下單代碼:
需要5個類.修改自官方demo.官方demo有很多是用不上的.
1.常量類.這個不要修改.來自微信官方.
import org.apache.http.client.HttpClient;
/**
* 常量
*/
public class WXPayConstants {
public enum SignType {
MD5, HMACSHA256
}
public static final String DOMAIN_API = "api.mch.weixin.qq.com";
public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";
public static final String FAIL = "FAIL";
public static final String SUCCESS = "SUCCESS";
public static final String HMACSHA256 = "HMAC-SHA256";
public static final String MD5 = "MD5";
public static final String FIELD_SIGN = "sign";
public static final String FIELD_SIGN_TYPE = "sign_type";
public static final String WXPAYSDK_VERSION = "WXPaySDK/3.0.9";
public static final String USER_AGENT = WXPAYSDK_VERSION +
" (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion();
public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
public static final String REFUND_URL_SUFFIX = "/secapi/pay/refund";
public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
public static final String REPORT_URL_SUFFIX = "/payitil/report";
public static final String SHORTURL_URL_SUFFIX = "/tools/shorturl";
public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";
// sandbox
public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
}
2.工具類,不要修改,來自微信官方
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import xx.WXPayConstants.SignType;
public class WXPayUtil {
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Random RANDOM = new SecureRandom();
/**
* XML格式字符串轉換爲Map
*
* @param strXML XML字符串
* @return XML數據轉換後的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
/**
* 將Map轉換爲XML格式的字符串
*
* @param data Map類型數據
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 生成帶有 sign 的 XML 格式字符串
*
* @param data Map類型數據
* @param key API密鑰
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
return generateSignedXml(data, key, SignType.MD5);
}
/**
* 生成帶有 sign 的 XML 格式字符串
*
* @param data Map類型數據
* @param key API密鑰
* @param signType 簽名類型
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key, SignType signType) throws Exception {
String sign = generateSignature(data, key, signType);
data.put(WXPayConstants.FIELD_SIGN, sign);
return mapToXml(data);
}
/**
* 判斷簽名是否正確
*
* @param xmlStr XML格式數據
* @param key API密鑰
* @return 簽名是否正確
* @throws Exception
*/
public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
Map<String, String> data = xmlToMap(xmlStr);
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key).equals(sign);
}
/**
* 判斷簽名是否正確,必須包含sign字段,否則返回false。使用MD5簽名。
*
* @param data Map類型數據
* @param key API密鑰
* @return 簽名是否正確
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
return isSignatureValid(data, key, SignType.MD5);
}
/**
* 判斷簽名是否正確,必須包含sign字段,否則返回false。
*
* @param data Map類型數據
* @param key API密鑰
* @param signType 簽名方式
* @return 簽名是否正確
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key, signType).equals(sign);
}
/**
* 生成簽名
*
* @param data 待簽名數據
* @param key API密鑰
* @return 簽名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, SignType.MD5);
}
/**
* 生成簽名. 注意,若含有sign_type字段,必須和signType參數保持一致。
*
* @param data 待簽名數據
* @param key API密鑰
* @param signType 簽名方式
* @return 簽名
*/
public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 參數值爲空,則不參與簽名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (SignType.MD5.equals(signType)) {
System.out.println("====|||"+sb.toString());
return MD5(sb.toString()).toUpperCase();
}
else if (SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 獲取隨機字符串 Nonce Str
*
* @return String 隨機字符串
*/
public static String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
/**
* 生成 MD5
*
* @param data 待處理數據
* @return MD5結果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
* @param data 待處理數據
* @param key 密鑰
* @return 加密結果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 日誌
* @return
*/
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
/**
* 獲取當前時間戳,單位秒
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* 獲取當前時間戳,單位毫秒
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
}
3.解析xml的工具類.來自微信官方,不要修改.
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
/**
* 2018/7/3
*/
public final class WXPayXmlUtil {
public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
return documentBuilderFactory.newDocumentBuilder();
}
public static Document newDocument() throws ParserConfigurationException {
return newDocumentBuilder().newDocument();
}
}
4.配置類.填上你自己的公衆號appid,商戶id和key
public class WxPayConfig {
public static final String APPID = "xxx";
public static final String MCH_ID ="";
public static final String KEY ="";
}
5.統一下單工具類.我寫好的,不需要修改具體邏輯,直接用就成了.當然你可以修改異常處理.這裏面異常都是拋的運行時異常.
import java.util.HashMap;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
/**
* 微信公衆號支付
*
* @author dmw
*
* 2018年9月17日
*/
public class WxMapPay {
/**
* 這裏面放參數
*/
public Map<String, String> map;
public static WxMapPay ready() {
WxMapPay wxMapPay = new WxMapPay();
Map<String, String> map = wxMapPay.map;
map.put("appid", WxPayConfig.APPID);
map.put("mch_id", WxPayConfig.MCH_ID);
map.put("trade_type", "JSAPI");
map.put("nonce_str", WXPayUtil.generateNonceStr());
return wxMapPay;
}
/**
* 加入商品描述
*
* @param nonce_str
* @return
*/
public WxMapPay setGoodsDiscription(String body) {
map.put("body", body);
return this;
}
/**
* 商品唯一訂單號
*
* @param out_trade_no
* @return
*/
public WxMapPay setOutTradeNo(String out_trade_no) {
map.put("out_trade_no", out_trade_no);
return this;
}
/**
* 商品價格 單位分
*
* @param total_fee
* @return
*/
public WxMapPay setTotalFee(int total_fee) {
map.put("total_fee", Integer.toString(total_fee));
return this;
}
/**
* 客戶端ip
*
* @param spbill_create_ip
* @return
*/
public WxMapPay setClientIp(String spbill_create_ip) {
map.put("spbill_create_ip", spbill_create_ip);
return this;
}
/**
* 回調網址 異步接收微信支付結果通知的回調地址,通知url必須爲外網可訪問的url,不能攜帶參數。
*
* @param notify_url
* @return
*/
public WxMapPay setNotifyUrl(String notify_url) {
map.put("notify_url", notify_url);
return this;
}
/**
* 回調網址 異步接收微信支付結果通知的回調地址,通知url必須爲外網可訪問的url,不能攜帶參數。
*
* @param notify_url
* @return
*/
public WxMapPay setOpenid(String openid) {
map.put("openid", openid);
return this;
}
/**
* 填其他
*
* @param key
* @param value
* @return
*/
public WxMapPay other(String key, String value) {
map.put(key, value);
return this;
}
public WxMapPayResult go() {
check();
String xml;
try {
xml = WXPayUtil.generateSignedXml(map, WxPayConfig.KEY);
} catch (Exception e1) {
e1.printStackTrace();
throw new RuntimeException("生成xml錯誤");
}
String repBody = http(xml);
try {
Map<String, String> xmltoMap = WXPayUtil.xmlToMap(repBody);
return new WxMapPayResult(xmltoMap);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("返回結果解析錯誤");
}
}
private String http(String body) {
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
BasicHttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(RegistryBuilder
.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory()).build(), null, null, null);
HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager).build();
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(8 * 1000).setConnectTimeout(6 * 1000)
.build();
httpPost.setConfig(requestConfig);
StringEntity postEntity = new StringEntity(body, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
try {
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
return EntityUtils.toString(httpEntity, "UTF-8");
} catch (Exception e) {
throw new RuntimeException("支付發送錯誤!");
}
}
private void check() {
if (!map.containsKey("nonce_str")) {
throw new RuntimeException("沒有nonce_str");
}
if (!map.containsKey("body")) {
throw new RuntimeException("沒有body");
}
if (!map.containsKey("out_trade_no")) {
throw new RuntimeException("沒有out_trade_no");
}
if (!map.containsKey("total_fee")) {
throw new RuntimeException("沒有total_fee");
}
if (!map.containsKey("spbill_create_ip")) {
throw new RuntimeException("沒有spbill_create_ip");
}
if (!map.containsKey("notify_url")) {
throw new RuntimeException("沒有notify_url");
}
if (!map.containsKey("openid")) {
throw new RuntimeException("沒有openid");
}
}
public String get(String key) {
return map.get(key);
}
private WxMapPay() {
super();
map = new HashMap<>();
}
public class WxMapPayResult{
private boolean reflag;
private Map<String,String> map;
public WxMapPayResult(Map<String, String> map) {
super();
this.map = map;
}
public boolean isHttpOk(){
String return_code = map.get("return_code");
if (return_code == null || !return_code.equals(WXPayConstants.SUCCESS)){
return false;
}
return true;
}
public boolean isTradeOk(){
String result_code = map.get("result_code");
if (result_code == null || !result_code.equals(WXPayConstants.SUCCESS)){
return false;
}
return true;
}
public Map<String,String> reSign(){
if (!reflag){
String prepay_id = map.get("prepay_id");
map.clear();
map.put("appId", WxPayConfig.APPID);
map.put("timeStamp", Long.toString(WXPayUtil.getCurrentTimestamp()));
map.put("package", "prepay_id="+prepay_id);
map.put("nonceStr", WXPayUtil.generateNonceStr());
map.put("signType", "MD5");
String signature = null;
try {
signature = WXPayUtil.generateSignature(map, WxPayConfig.KEY);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("簽名錯誤了又");
}
map.put("paySign", signature);
reflag = true;
return map;
}
throw new RuntimeException("已經重新生成了,不能再生成");
}
}
}
6.怎麼使用?就這樣使用
WxMapPayResult result =
WxMapPay
//初始化,加載配置
.ready()
//設置客戶端ip
.setClientIp(remoteAddr)
//設置商品描述
.setGoodsDiscription("團購")
//設置回調通知
.setNotifyUrl("http://你的交易成功通知url")
//設置openid
.setOpenid(openid)
//設置本地的訂單編號
.setOutTradeNo(id)
//設置交易金額,但位分
.setTotalFee(1).
//下單去
go();
/**
* 如果http請求狀態,且交易狀態ok
*/
if (result.isHttpOk() && result.isTradeOk()){
//返回前臺需要的加密數據
Map<String, String> map = result.reSign();
}
說明:
1. WxMapPay的構造是私有的,你只能使用靜態ready()來初始化加載配置,然後去設置必須要設置的一些屬性.比如手機端的客戶端ip,商品描述,回調通知啊等.如果你還想添加其他的,這裏面有一個other(String key,String value)方法,請根據自行需要和微信開發文檔來添加.必須設置的屬性的方法名都是以set開頭的,所以你很容易把他們一個個給找到.如果你set不完,程序會自動檢查報錯的.
2.關於客戶端的ip.必須是真實ip,請注意如果你使用了nginx,要記得把真實ip傳過來.否則微信可能檢查ip拒絕調起支付窗口.
3.WxMapPayResult 有兩個ok狀態,一個是http狀態ok,一個是統一下單狀態ok,這倆你都得判斷.
4.返回的map裏包括了
appId,
timeStamp,
nonceStr,
package,
signType,
paySign
這六個內容,如果你看了微信jsapi的支付就應該明白,這六個正好是所需要的.
然後把這個map傳遞到前臺就行了.
5.設置set商品描述時.它有它固有的名字規則.請看.
二. 前臺該怎麼寫?
兩種寫法.一種是使用微信jssdk.一種是直接使用微信內置瀏覽器對象.它倆什麼關係?應該是jssdk裏引用了微信內置瀏覽器對象吧.
使用微信內置瀏覽器對象就意味着必須在微信裏才能使用.這並不是在其他應用裏喚醒微信並打開微信支付 .
1.jssdk寫法.
這個是在使用jssdk需要進行初始化驗籤保證安全.這個大家都清楚.如果不懂,那麼去看另外一種方法吧.
wx.config({
//debug: true,
appId: "",
timestamp: "",
nonceStr: "",
signature: "",
jsApiList: ["chooseWXPay"]
});
你還要引入一個js.微信的js
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
驗完後調用這個就行了.
wx.chooseWXPay({
appId: data.body.appId,
timestamp: data.body.timeStamp,
nonceStr: data.body.nonceStr,
package: data.body.package,
signType: data.body.signType,
paySign: data.body.paySign,
success: function (res) {
alert("成功");
},
fail: function(res) {
alert('fail--'+JSON.stringify(res))
},
complete: function(res) {
alert('complete--'+JSON.stringify(res))
}
})
這個裏面.data.body.xxx就是上面那1.6裏說的那個map裏面的值.
不管怎樣,都會走complete方法.
如果用戶確認支付了,會走success方法.
如果調用api錯誤了.那麼會走fail方法.
2.微信內置對象寫法
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
"appId":data.body.appId, //公衆號名稱,由商戶傳入
"timeStamp":data.body.timeStamp, //時間戳,自1970年以來的秒數
"nonceStr":data.body.nonceStr, //隨機串
"package":data.body.package,
"signType":data.body.signType, //微信簽名方式:
"paySign":data.body.paySign //微信簽名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ){
// 使用以上方式判斷前端返回,微信團隊鄭重提示:
//res.err_msg將在用戶支付成功後返回ok,但並不保證它絕對可靠。
}
}
);
這個裏面.data.body.xxx就是上面那1.6裏說的那個map裏面的值.
3.一些返回fail的坑?
1.對於安卓來說,不管什麼都是fail.所以請使用ios來調試,ios會告訴你到底怎麼回事.
2.微信支付授權目錄要配置正確(我目前是沒有這個問題的).timeStamp是要大寫的,但如果你用了我的工具類,也是不會出現這樣的問題的..
3.客戶端ip要真實.
4.如果使用jssdk寫法.請引入jweixin-1.0.0.js
5.如果ios調試沒有問題,而安卓有問題(或部分機型有問題?),那麼你應該使用的是jssdk,你引入的那個jweixin-1.0.0.js放在了下面,應該把這條js放到最上面來引用.
三.回調
public String notifyUrlByWx(HttpServletRequest request) throws Exception {
String notifyData = WebUtils.getPostBody(request);//獲取輸入流xml字符串
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData); // 轉換成map
if (WXPayUtil.isSignatureValid(notifyMap, WxPayConfig.KEY)) {
// 簽名正確
// 進行處理。
// 注意特殊情況:訂單已經退款,但收到了支付結果成功的通知,不應把商戶側訂單狀態從退款改成支付成功
String resultCode = notifyMap.get("result_code");
if (resultCode.equals("SUCCESS")){
//處理業務邏輯
//返回
return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}else{
//如果支付失敗
}
} else {
// 簽名錯誤,如果數據裏沒有sign字段,也認爲是簽名錯誤
}
return "error";
}