開篇第一件事 說微信伙菊,這是一個(gè)怎樣的平臺(tái),做完了uni-app 小程序端的微信支付 蛀恩,心想app還不是信手拈來疫铜??双谆?然而事實(shí)就是 支付簽名驗(yàn)證失敗???? 是的一搜才發(fā)現(xiàn) 全是罵微信的壳咕,這樣才好受點(diǎn)席揽,嘗試了各種方法,后臺(tái)也是焦頭爛額谓厘,我也不細(xì)說了幌羞,看到的都試了,大小寫竟稳,二次加密属桦,時(shí)間戳10位,簽名驗(yàn)證他爸。等等聂宾。。诊笤。都沒用 系谐,最后就從以前一個(gè)同事那里 復(fù)制了一份代碼,我們是java后臺(tái) 讨跟,然后就好了 直接上代碼吧 我全部復(fù)制 無所謂的纪他。 好像jsapi支付不可以用來app支付
package com.pop121.server.service.impl.api;
import com.pop121.server.entity.TOtoCommodityOrder;
import com.pop121.server.entity.dto.CommodityOrderInfo;
import com.pop121.server.service.business.OrderBusinessServiceImpl;
import com.pop121.server.util.HttpUtils;
import com.usejee.util.DateUtil;
import com.usejee.util.IpUtils;
import com.usejee.util.StringUtil;
import com.usejee.util.crypto.MD5Util;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.text.ParseException;
import java.util.*;
/**
?* APP微信付款
?* Created by 楊建亮
?* on 2017/8/6.
?*/
@Service
public class WeixinPayServiceImpl {
??? private static final Logger LOG = LoggerFactory.getLogger(WeixinPayServiceImpl.class);
??? String AppId = "";
??? String paternerKey = ""; //商戶api密鑰
??? String mch_id = ""; //微信支付分配的商戶號(hào)
??? String notify_url = "";// 統(tǒng)一下單后微信回調(diào)通知url
??? String weixinPayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";// 統(tǒng)一下單url
??? String weixinPayQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";// 查詢訂單
??? @Autowired
??? private OrderBusinessServiceImpl orderBusinessService;
??? /**
???? * 向微信支付請(qǐng)求統(tǒng)一下單
???? */
??? public String unifiedorder(HttpServletRequest request, CommodityOrderInfo orderDTO) {
??????? String realCost = orderDTO.getOrder_total_price(); //金額,單位分
??????? BigDecimal cost = new BigDecimal(realCost);
??????? int sendCost = (cost.multiply( new BigDecimal(100) ) ).intValue();//轉(zhuǎn)化為分
??????? String orderid = orderDTO.getOrder_id(); //下單批次號(hào)晾匠。本地系統(tǒng)生成多條記錄茶袒,批次號(hào)一樣。
??????? Map<String, String> reqMap = new LinkedHashMap<>();
??????? reqMap.put("appid", AppId);
//??????? reqMap.put("attach", attach); //暫無附加數(shù)據(jù)
??????? reqMap.put("body", "1111 - 購買會(huì)員 " + orderDTO.getProduct_name());
??????? reqMap.put("mch_id", mch_id); //微信支付分配的商戶號(hào)
??????? reqMap.put("nonce_str", create_nonce_str());
??????? reqMap.put("notify_url", notify_url);// 此路徑是微信服務(wù)器調(diào)用支付結(jié)果通知路徑
??????? reqMap.put("out_trade_no", orderid); //
??????? reqMap.put("spbill_create_ip", IpUtils.getRemoteAddr(request));
??????? reqMap.put("total_fee", String.valueOf(sendCost)); //訂單總金額混聊,單位為分
??????? reqMap.put("trade_type", "APP");
??????? String sign = getSign(reqMap, paternerKey); //以上參數(shù)通過參數(shù)名ASCII字典序排序
??????? reqMap.put("sign", sign);
??????? String reqXml = mapToXml(reqMap);
??????? LOG.debug("send req Xml ======>{}", reqXml);
?????? // System.out.println("send req Xml ======>{}"+ reqXml);
??????? String xmlResponse = HttpUtils.doPostXml(weixinPayUrl, reqXml); //發(fā)送給微信下單
??????? LOG.debug("get Response Xml ======>{}", xmlResponse);
????? //? System.out.println("get Response Xml ======>{}"+ xmlResponse);
??????? return convertToAppForm( xmlResponse );
??? }
??? /**
???? * 接收微信支付返回異步通知消息
???? */
??? public String async(HttpServletRequest request) {
??????? Map<String, String> paramsMap = null;
??????? Map<String, String> sortMap = new LinkedHashMap<>(); //對(duì)paramsMap的key字母排序弹谁,去除sign參數(shù)
??????? boolean signVerfied = false;
??????? String msg = StringUtil.EMPTY;
??????? try {
??????????? InputStream inputStream = request.getInputStream();
??????????? String resp = IOUtils.toString(inputStream, "UTF-8");
??????????? LOG.debug(resp);
??????????? paramsMap = xmlToMap(resp); // 得到微信發(fā)送來到參數(shù)map
??????????? String result_code = paramsMap.get("result_code"); // 微信通知返回碼
??????????? String return_msg = paramsMap.get("return_msg"); // 返回信息
??????????? if( !result_code.equals("SUCCESS") ){
??????????????? throw new RuntimeException(return_msg);
??????????? }
??????????? //以上參數(shù)通過參數(shù)名ASCII字典序排序
??????????? Collection<String> keyset = paramsMap.keySet();
??????????? List<String> list = new ArrayList<>(keyset);
??????????? Collections.sort(list);
??????????? for (int i = 0; i < list.size(); i++) {
??????????????? String key = list.get(i);
??????????????? if( !"sign".equalsIgnoreCase(key) ){
??????????????????? sortMap.put(key, paramsMap.get(key));
??????????????? }
??????????? }
//??????????? LOG.debug(JSONUtil.toJson(sortMap));
??????????? String returnSign = paramsMap.get("sign");
??????????? String total_fee = paramsMap.get("total_fee");//金額
??????????? String mySign = getSign(sortMap, paternerKey);
??????????? signVerfied = mySign.equalsIgnoreCase(returnSign);//驗(yàn)證sign簽名是否正確
??????????? //校驗(yàn)返回的訂單金額是否與商戶側(cè)的訂單金額一致?
??????????? //todo query db compare total_fee
??????? } catch (Exception e) {
??????????? LOG.error("{}", e);
??????????? throw new RuntimeException(e);
??????? }
??????? if (signVerfied) {// 驗(yàn)證成功
??????????? String trade_status = paramsMap.get("result_code"); // 交易狀態(tài)碼
??????????? String order_no = paramsMap.get("out_trade_no"); // 訂單號(hào)
??????????? String trade_no = paramsMap.get("transaction_id"); // 交易號(hào)
??????????? if ( trade_status.equals("SUCCESS") ) {
??????????????? //支付成功
??????????????? // 更新本系統(tǒng)中數(shù)據(jù)庫里的訂單數(shù)據(jù)狀態(tài)句喜,標(biāo)記為交易支付完成预愤。
??????????????? orderBusinessService.updateDbOrderToPayment(order_no, trade_no);
??????????????? msg = "<xml>\n" +
??????????????????????? "?? <return_code><![CDATA[SUCCESS]]></return_code>\n" +
??????????????????????? "?? <return_msg><![CDATA[OK]]></return_msg>\n" +
??????????????????????? "</xml>\n";
??????????? }else {
??????????????? // 支付失敗
??????????????? String err_code = paramsMap.get("err_code"); //
??????????????? String err_code_des = paramsMap.get("err_code_des"); //
??????????????? throw new RuntimeException( err_code_des + " [ " + err_code + " ]");
??????????? }
??????? } else {// 驗(yàn)證失敗
??????????? throw new RuntimeException("簽名錯(cuò)誤");
??????? }
??????? return msg;
??? }
??? /**
???? * 主動(dòng)查詢訂單并更新本地狀態(tài)
???? * @param commodityOrder
???? * @return
???? */
??? public void updateOrderStatusByWeixinPay(TOtoCommodityOrder commodityOrder){
??????? Map<String, String> reqMap = new LinkedHashMap<>();
??????? reqMap.put("appid", AppId);
??????? reqMap.put("mch_id", mch_id); //微信支付分配的商戶號(hào)
??????? reqMap.put("nonce_str", create_nonce_str());
??????? if( StringUtil.isBlank(commodityOrder.getTransId()) ){
??????????? reqMap.put("out_trade_no", commodityOrder.getOrderId());
??????? }else {
??????????? reqMap.put("transaction_id", commodityOrder.getTransId());
??????? }
??????? String sign = getSign(reqMap, paternerKey); //以上參數(shù)通過參數(shù)名ASCII字典序排序
??????? reqMap.put("sign", sign);
??????? String reqXml = mapToXml(reqMap);
??????? LOG.debug("send req Xml ======>{}", reqXml);
??????? String xmlResponse = HttpUtils.doPostXml(weixinPayQueryUrl, reqXml);
??????? LOG.debug("get Response Xml ======>{}", xmlResponse);
??????? Map<String, String> weixinMap = xmlToMap(xmlResponse);
??????? if("SUCCESS".equalsIgnoreCase(weixinMap.get("return_code"))){
??????????? String trade_state = weixinMap.get("trade_state");
??????????? if("SUCCESS".equalsIgnoreCase(trade_state)){
??????????????? orderBusinessService.updateDbOrderToPayment(commodityOrder.getOrderId(), commodityOrder.getTransId());
??????????????? //只更新本次查詢的對(duì)象,數(shù)據(jù)庫交給異步通知處理咳胃。
??????????? }
??????? }else {
??????????? LOG.error("微信支付訂單信息查詢失斨部怠!");
//??????????? throw new RuntimeException("微信支付訂單信息查詢失斦剐浮销睁!");
??????? }
??? }
??? /**
???? * 把微信統(tǒng)一下單返回來的數(shù)據(jù),重新組織成APP客戶端發(fā)起支付需要的參數(shù)存崖。
???? */
??? private String convertToAppForm(String xmlResponse){
??????? Map<String, String> weixinMap = xmlToMap(xmlResponse);
??????? if("SUCCESS".equalsIgnoreCase(weixinMap.get("return_code"))){
??????????? Map<String, String> clientMap = new LinkedHashMap<>();//簽名按key字母順序
??????????? clientMap.put("appid", weixinMap.get("appid")==null?"":weixinMap.get("appid"));//客戶端如果取不到appid值冻记,說明服務(wù)器有錯(cuò)。
??????????? clientMap.put("noncestr", weixinMap.get("nonce_str"));
??????????? clientMap.put("package", "Sign=WXPay");
??????????? clientMap.put("partnerid", weixinMap.get("mch_id"));
??????????? clientMap.put("prepayid", weixinMap.get("prepay_id"));
??????????? String seconds = StringUtil.EMPTY;
??????????? try {
??????????????? Date startDate = DateUtil.parseDate("1970-01-01 00:00:00");
??????????????? seconds = String.valueOf( (new Date()).getTime() - startDate.getTime() / 1000).substring(0, 10);
??????????? } catch (ParseException e) {
??????????????? LOG.error("{}", e);
??????????? }
??????????? clientMap.put("timestamp", seconds);//標(biāo)準(zhǔn)北京時(shí)間来惧,時(shí)區(qū)為東八區(qū)冗栗,自1970年1月1日 0點(diǎn)0分0秒以來的秒數(shù)。注意:部分系統(tǒng)取到的值為毫秒級(jí),需要轉(zhuǎn)換成秒(10位數(shù)字)隅居。
??????????? String sign = StringUtil.isBlank(seconds)?StringUtil.EMPTY:getSign(clientMap, paternerKey);
??????????? clientMap.put("sign", sign);
//??????????? String json = JSON.toJSONString(clientMap);
??????????? return buildUrlParamStr(clientMap).toString();
??????? }else {
??????????? return StringUtil.EMPTY;
??????? }
??? }
??? private String create_timestamp() {
??????? return Long.toString(System.currentTimeMillis() / 1000);
??? }
??? private String create_nonce_str() {
??????? String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
??????? StringBuilder res = new StringBuilder();
??????? for (int i = 0; i < 16; i++) {
??????????? Random rd = new Random();
??????????? res.append( chars.charAt(rd.nextInt(chars.length() - 1)) );
??????? }
??????? return res.toString();
??? }
??? public StringBuilder buildUrlParamStr(Map<String, String> params) {
??????? StringBuilder string1 = new StringBuilder();
??????? for (Map.Entry<String, String> entry : params.entrySet()) {
??????????? if (StringUtils.isNotBlank(entry.getValue())) { //如果參數(shù)的值為空不參與簽名钠至;
??????????????? if (StringUtils.isNotBlank(string1)) {
??????????????????? string1.append( "&" );
??????????????? }
??????????????? string1.append( entry.getKey() ).append( "=" ).append( entry.getValue() );
??????????? }
??????? }
??????? return string1;
??? }
??? public String getSign(Map<String, String> params, String paternerKey) {
??????? StringBuilder string1 = buildUrlParamStr(params);
??????? String stringSignTemp = string1.append("&key=" ).append( paternerKey ).toString();
??????? LOG.debug(stringSignTemp);
??????? return MD5Util.MD5(stringSignTemp).toUpperCase();
??? }
??? public static String createSign(Map<String, String> params, String partnerKey) { //key為商戶平臺(tái)設(shè)置的密鑰key
??????? // 生成簽名前先去除sign
??????? params.remove("sign");
??????? String stringA = packageSign(params, false);
??????? String stringSignTemp = stringA + "&key=" + partnerKey;
??????? return md5(stringSignTemp).toUpperCase();
??? }
??? public static String md5(String srcStr){
??????? return hash("MD5", srcStr);
??? }
??? public static String hash(String algorithm, String srcStr) {
??????? try {
??????????? MessageDigest md = MessageDigest.getInstance(algorithm);
??????????? byte[] bytes = md.digest(srcStr.getBytes("utf-8"));
??????????? return toHex(bytes);
??????? }
??????? catch (Exception e) {
??????????? throw new RuntimeException(e);
??????? }
??? }
??? public static String packageSign(Map<String, String> params, boolean urlEncoder) {
??????? // 先將參數(shù)以其參數(shù)名的字典序升序進(jìn)行排序
??????? TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
??????? // 遍歷排序后的字典,將所有參數(shù)按"key=value"格式拼接在一起
??????? StringBuilder sb = new StringBuilder();
??????? boolean first = true;
??????? for (Map.Entry<String, String> param : sortedParams.entrySet()) {
??????????? String value = param.getValue();
??????????? if (isBlank(value)) {
??????????????? continue;
??????????? }
??????????? if (first) {
??????????????? first = false;
??????????? } else {
??????????????? sb.append("&");
??????????? }
??????????? sb.append(param.getKey()).append("=");
??????????? if (urlEncoder) {
??????????????? try {
??????????????????? value = urlEncode(value);
??????????????? } catch (UnsupportedEncodingException e) {
??????????????? }
??????????? }
??????????? sb.append(value);
??????? }
??????? return sb.toString();
??? }
??? public static String urlEncode(String src) throws UnsupportedEncodingException {
??????? return URLEncoder.encode(src, Charsets.UTF_8.name()).replace("+", "%20");
??? }
??? public static boolean isBlank(String str) {
??????? if (str == null) {
??????????? return true;
??????? }
??????? int len = str.length();
??????? if (len == 0) {
??????????? return true;
??????? }
??????? for (int i = 0; i < len; i++) {
??????????? switch (str.charAt(i)) {
??????????????? case ' ':
??????????????? case '\t':
??????????????? case '\n':
??????????????? case '\r':
??????????????????? // case '\b':
??????????????????? // case '\f':
??????????????????? break;
??????????????? default:
??????????????????? return false;
??????????? }
??????? }
??????? return true;
??? }
??? private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
??? private static String toHex(byte[] bytes) {
??????? StringBuilder ret = new StringBuilder(bytes.length * 2);
??????? for (int i=0; i<bytes.length; i++) {
??????????? ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
??????????? ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
??????? }
??????? return ret.toString();
??? }
??? /**
???? * map轉(zhuǎn)成xml
???? *
???? * @param arr
???? * @return
???? */
??? public static String mapToXml(Map<String, String> arr) {
??????? StringBuffer xml = new StringBuffer("<xml>");
??????? for (Map.Entry<String, String> entry : arr.entrySet()) {
??????????? String key = entry.getKey();
??????????? String val = entry.getValue();
??????????? xml.append("<").append(key).append(">").append(val).append("</").append(key).append(">");
??????? }
??????? xml.append("</xml>");
??????? return xml.toString();
??? }
??? /**
???? * 解析xml 為Map
???? */
??? public static Map<String, String> xmlToMap(String xml) {
??????? if (StringUtils.isBlank(xml)) {
??????????? return Collections.EMPTY_MAP;
??????? }
??????? InputStream inputStream = null;
??????? try {
??????????? inputStream = new ByteArrayInputStream(xml.getBytes("utf-8"));
??????? } catch (UnsupportedEncodingException e) {
??????????? LOG.error("parseXmlToMap error : {}", e);
??????????? throw new RuntimeException(e);
??????? }
??????? Map<String, String> map = new HashMap<>();
??????? XMLInputFactory factory = XMLInputFactory.newInstance();
??????? XMLStreamReader reader = null;
??????? try {
??????????? reader = factory.createXMLStreamReader(inputStream, "utf-8");
??????????? while (reader.hasNext()) {
??????????????? int type = reader.next();
??????????????? if (type == XMLStreamConstants.START_ELEMENT) {
??????????????????? String tagName = reader.getName().toString();
??????????????????? if (!"xml".equalsIgnoreCase(tagName)) { //沒有內(nèi)容的節(jié)點(diǎn)調(diào)用一下方法會(huì)報(bào)錯(cuò)
??????????????????????? String val = reader.getElementText();
//??????????????????????????? System.out.println( tagName + "==" + val);
??????????????????????? map.put(tagName, val);
??????????????????? }
??????????????? }
??????????? }
??????????? return map;
??????? } catch (Exception e) {
??????????? LOG.error("parseXmlToMap error : {}", e);
??????????? throw new RuntimeException(e);
??????? } finally {
??????????? if (reader != null) {
??????????????? try {
??????????????????? reader.close();
??????????????? } catch (XMLStreamException e) {
??????????????????? LOG.error("parseXmlToMap error : {}", e);
??????????????? }
??????????? }
??????????? if (inputStream != null) {
??????????????? try {
??????????????????? inputStream.close();
??????????????? } catch (IOException e) {
??????????????????? LOG.error("parseXmlToMap error : {}", e);
??????????????? }
??????????? }
??????? }
??? }
??? /**
???? * 向微信支付請(qǐng)求統(tǒng)一下單
???? */
??? public String tradePrecreatePay(HttpServletRequest request, CommodityOrderInfo orderDTO) {
??????? String realCost = orderDTO.getOrder_total_price(); //金額胎源,單位分
??????? BigDecimal cost = new BigDecimal(realCost);
??????? int sendCost = (cost.multiply( new BigDecimal(100) ) ).intValue();//轉(zhuǎn)化為分
??????? String orderid = orderDTO.getOrder_id(); //下單批次號(hào)棉钧。本地系統(tǒng)生成多條記錄,批次號(hào)一樣涕蚤。
??????? Map<String, String> reqMap = new LinkedHashMap<>();
??????? reqMap.put("appid", AppId);
??????? reqMap.put("mch_id", mch_id); //微信支付分配的商戶號(hào)
??????? reqMap.put("attach","康兮運(yùn)動(dòng)");//attach 附加數(shù)據(jù)
??????? reqMap.put("body", "康兮 - 購買會(huì)員 " + orderDTO.getProduct_name());
??????? reqMap.put("nonce_str", create_nonce_str());
??????? reqMap.put("out_trade_no", orderid); //
??????? reqMap.put("trade_type", "NATIVE");
??????? reqMap.put("product_id", orderDTO.getProductId().toString());
??????? reqMap.put("total_fee", String.valueOf(sendCost)); //訂單總金額宪卿,單位為分
??????? reqMap.put("notify_url", notify_url);// 此路徑是微信服務(wù)器調(diào)用支付結(jié)果通知路徑
??????? reqMap.put("spbill_create_ip", IpUtils.getRemoteAddr(request));
??????? reqMap.put("time_stamp",String.valueOf(System.currentTimeMillis()));
?????? // String sign = getSign(reqMap, paternerKey); //以上參數(shù)通過參數(shù)名ASCII字典序排序
??????? String createSign=createSign(reqMap, paternerKey);
//??????? reqMap.put("attach", attach); //暫無附加數(shù)據(jù)
??????? reqMap.put("sign", createSign);
??????? String reqXml = mapToXml(reqMap);
??????? LOG.debug("send req Xml ======>{}", reqXml);
??????? // System.out.println("send req Xml ======>{}"+ reqXml);
??????? String xmlResponse = HttpUtils.doPostXml(weixinPayUrl, reqXml); //發(fā)送給微信下單
??????? LOG.debug("get Response Xml ======>{}", xmlResponse);
??????? //? System.out.println("get Response Xml ======>{}"+ xmlResponse);
??????? Map<String, String> weixinMap = xmlToMap(xmlResponse);
??????? String qr_code =weixinMap.get("code_url");
??????? return qr_code;
??? }
}
? 然后調(diào)取 就沒問題了。其實(shí)就那幾個(gè)參數(shù) 多數(shù)? 多數(shù)是后臺(tái)出錯(cuò)了