开篇第一件事 说微信,这是一个怎样的平台,做完了uni-app 小程序端的微信支付 ,心想app还不是信手拈来???然而事实就是 支付签名验证失败 是的一搜才发现 全是骂微信的,这样才好受点,尝试了各种方法,后台也是焦头烂额,我也不细说了,看到的都试了,大小写,二次加密,时间戳10位,签名验证。等等。。。都没用 ,最后就从以前一个同事那里 复制了一份代码,我们是java后台 ,然后就好了 直接上代码吧 我全部复制 无所谓的。 好像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 = ""; //微信支付分配的商户号
String notify_url = "";// 统一下单后微信回调通知url
String weixinPayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";// 统一下单url
String weixinPayQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";// 查询订单
@Autowired
private OrderBusinessServiceImpl orderBusinessService;
/**
* 向微信支付请求统一下单
*/
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();//转化为分
String orderid = orderDTO.getOrder_id(); //下单批次号。本地系统生成多条记录,批次号一样。
Map<String, String> reqMap = new LinkedHashMap<>();
reqMap.put("appid", AppId);
// reqMap.put("attach", attach); //暂无附加数据
reqMap.put("body", "1111 - 购买会员 " + orderDTO.getProduct_name());
reqMap.put("mch_id", mch_id); //微信支付分配的商户号
reqMap.put("nonce_str", create_nonce_str());
reqMap.put("notify_url", notify_url);// 此路径是微信服务器调用支付结果通知路径
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); //以上参数通过参数名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); //发送给微信下单
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<>(); //对paramsMap的key字母排序,去除sign参数
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); // 得到微信发送来到参数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);
}
//以上参数通过参数名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);//验证sign签名是否正确
//校验返回的订单金额是否与商户侧的订单金额一致?
//todo query db compare total_fee
} catch (Exception e) {
LOG.error("{}", e);
throw new RuntimeException(e);
}
if (signVerfied) {// 验证成功
String trade_status = paramsMap.get("result_code"); // 交易状态码
String order_no = paramsMap.get("out_trade_no"); // 订单号
String trade_no = paramsMap.get("transaction_id"); // 交易号
if ( trade_status.equals("SUCCESS") ) {
//支付成功
// 更新本系统中数据库里的订单数据状态,标记为交易支付完成。
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 {// 验证失败
throw new RuntimeException("签名错误");
}
return msg;
}
/**
* 主动查询订单并更新本地状态
* @param commodityOrder
* @return
*/
public void updateOrderStatusByWeixinPay(TOtoCommodityOrder commodityOrder){
Map<String, String> reqMap = new LinkedHashMap<>();
reqMap.put("appid", AppId);
reqMap.put("mch_id", mch_id); //微信支付分配的商户号
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); //以上参数通过参数名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());
//只更新本次查询的对象,数据库交给异步通知处理。
}
}else {
LOG.error("微信支付订单信息查询失败!");
// throw new RuntimeException("微信支付订单信息查询失败!");
}
}
/**
* 把微信统一下单返回来的数据,重新组织成APP客户端发起支付需要的参数。
*/
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值,说明服务器有错。
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);//标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。
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())) { //如果参数的值为空不参与签名;
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为商户平台设置的密钥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) {
// 先将参数以其参数名的字典序升序进行排序
TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
// 遍历排序后的字典,将所有参数按"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转成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)) { //没有内容的节点调用一下方法会报错
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);
}
}
}
}
/**
* 向微信支付请求统一下单
*/
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();//转化为分
String orderid = orderDTO.getOrder_id(); //下单批次号。本地系统生成多条记录,批次号一样。
Map<String, String> reqMap = new LinkedHashMap<>();
reqMap.put("appid", AppId);
reqMap.put("mch_id", mch_id); //微信支付分配的商户号
reqMap.put("attach","康兮运动");//attach 附加数据
reqMap.put("body", "康兮 - 购买会员 " + 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);// 此路径是微信服务器调用支付结果通知路径
reqMap.put("spbill_create_ip", IpUtils.getRemoteAddr(request));
reqMap.put("time_stamp",String.valueOf(System.currentTimeMillis()));
// String sign = getSign(reqMap, paternerKey); //以上参数通过参数名ASCII字典序排序
String createSign=createSign(reqMap, paternerKey);
// reqMap.put("attach", attach); //暂无附加数据
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); //发送给微信下单
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;
}
}
然后调取 就没问题了。其实就那几个参数 多数 多数是后台出错了
网友评论