微信小程序支付中遇到的问题
1.首先看看微信官方对于微信小程序支付的介绍:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1
2.从微信官方我们可以看到 微信需要的参数是xml格式的数据,因此第一步我们就需要一个将参数从实体转换为xml格式的工具类:方法的代码如下:
package cn.com.webi.paycommon.base.javabase.util;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
/**
* Title: XstreamUtil<br>
* Description: XstreamUtil<br>
* Company:</br>
* CreateDate:2019年01月30日 10:14
*
* @author james.fxy
*/
public class XstreamUtil {
/**
* Title: 对象转换为xml<br>
* Description: objectToXml<br>
* CreateDate: 2019/1/30 10:16<br>
*
* @category 对象转换为xml
* @author james.fxy
* @param obj
* @return java.lang.String
* @exception Exception
*/
public static String objectToXml(Object obj) {
/**
* 双下划线转为单下划线
*/
XStream xStream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
// xstream使用注解转换
xStream.alias("xml", obj.getClass());
// 去除属性
xStream.autodetectAnnotations(true);
xStream.processAnnotations(obj.getClass());
return xStream.toXML(obj);
}
/**
* Title: xml转换为对象<br>
* Description: xmlToObject<br>
* CreateDate: 2019/1/30 10:16<br>
*
* @category xml转换为对象
* @author james.fxy
* @param xml
* * @param cls
* @return T
* @exception Exception
*/
public static <T> T xmlToObject(String xml, Class<T> cls) {
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", cls);
// xstream使用注解转换
xstream.processAnnotations(cls);
// 去除属性
xstream.autodetectAnnotations(true);
return (T) xstream.fromXML(xml);
}
}
工具类主要的作用是为了实体与xml格式的数据进行转换。
3.需要组出提交到微信后台的支付数据
我们的实体类如下:其中
@XStreamAlias("mch_id") @XStreamAlias是给实体属性起的xml格式的别名(必须与微信的请求格式的参数一致)
package cn.com.webi.paycenter.wechatminiprogram.param;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
/**
* Title: 请求到微信后台的数据<br>
* Description: 请求到微信后台的数据<br>
* Company:</br>
* CreateDate:2019年01月30日 13:34
*
* @author james.fxy
*/
@Data
public class MiniProgramOrderRequestParam {
/**
* 小程序ID
*/
private String appid;
/**
* 商户号
*/
@XStreamAlias("mch_id")
private String mchId;
/**
* 随机字符串
*/
@XStreamAlias("nonce_str")
private String nonceStr;
/**
* 签名类型
*/
@XStreamAlias("sign_type")
private String signType;
/**
* 签名
*/
private String sign;
/**
* 商品描述
*/
private String body;
/**
* 商户订单号
*/
@XStreamAlias("out_trade_no")
private String outTradeNo;
/**
* 标价金额 ,单位为分
*/
@XStreamAlias("total_fee")
private int totalFee;
/**
* 终端IP
*/
@XStreamAlias("spbill_create_ip")
private String spbillCreateIp;
/**
* 通知地址
*/
@XStreamAlias("notify_url")
private String notifyUrl;
/**
* 交易类型
*/
@XStreamAlias("trade_type")
private String tradeType;
/**
* 用户标识
*/
private String openid;
}
将实体转换为xml
/**
* Title: 小程序提交数据请求到微信后台返回数据<br>
* Description: 小程序提交数据请求到微信后台返回数据<br>
* CreateDate: 2019/1/31 9:47<br>
*
* @category 小程序提交数据请求到微信后台返回数据
* @author james.fxy
* @param miniProgramParamToWepay
* @return java.lang.String
* @exception Exception
*/
private String getMiniProgramResultXml(MiniProgramParamToWePay miniProgramParamToWepay)
throws Exception {
/**
* 根据订单号查询金蝶CODE再联查小程序信息表得到小程序相关信息
*/
OrderResourcePartParam orderResourceParam = wechatProgrammerMapper
.findMiniProgramInfoByResourceId(miniProgramParamToWepay.getOrderResourceId());
if (isNull(orderResourceParam)) {
throw new BaseErrorException(ErrorCodeEnum.ORDER_RESOURCE_PARAM_IS_EMPTY);
}
// 通过金蝶code查询微信小程序初始化数据(从缓存获取)
WechatMiniProgram wechatMiniProgram = getObjectFromRedis(orderResourceParam
.getNewKingdeeCode());
if (isNull(wechatMiniProgram)) {
throw new BaseErrorException(ErrorCodeEnum.ORDER_RESOURCE_PARAM_IS_EMPTY);
}
MiniProgramOrderRequestParam miniProgramOrderRequestParam = new MiniProgramOrderRequestParam();
miniProgramOrderRequestParam.setAppid(miniProgramParamToWepay.getAppid());
miniProgramOrderRequestParam.setMchId(wechatMiniProgram.getMchId());
// 业务系统的传入以元为单位,微信后台以分为单位需要乘以100
miniProgramOrderRequestParam.setTotalFee(orderResourceParam.getTotalFee().multiply(
new BigDecimal(100)).intValue());
miniProgramOrderRequestParam.setBody(orderResourceParam.getOrderContent());
miniProgramOrderRequestParam.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
miniProgramOrderRequestParam.setOutTradeNo(RandomStringGenerator.getRandomStringByLength(32));
miniProgramOrderRequestParam.setNotifyUrl(wePayPortalPath
+ WechatMiniProgramConstant.NOTIFY_URL);
miniProgramOrderRequestParam.setOpenid(miniProgramParamToWepay.getOpenid());
miniProgramOrderRequestParam.setSignType(WechatMiniProgramConstant.SIGN_TYPE);
miniProgramOrderRequestParam.setSpbillCreateIp(miniProgramParamToWepay.getSpbillCreateIp());
miniProgramOrderRequestParam.setTradeType(WechatMiniProgramConstant.TRADE_TYPE);
String sign = Signature.getSign(miniProgramOrderRequestParam,
wechatMiniProgram
.getSecretKey());
miniProgramOrderRequestParam.setSign(sign);
return XstreamUtil.objectToXml(miniProgramOrderRequestParam);
}
这样就获取到了需提交到微信后台支付的xml格式数据
注意:这个里有一个问题就是签名,微信默认的签名类型是MD5
我们需要传入需要签名的属性,使用的签名的方法工具,这里面需要传入商户的秘钥进行加签。
package cn.com.webi.paycenter.common;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
/**
* Title: 签名算法<br>
* Description: <br>
* CreateDate: 2019/1/30 11:15<br>
*
* @category 签名算法
* @author james.fxy
* @param
* @return
* @exception Exception
*/
@Slf4j
public class Signature {
/**
* Title: 对象签名<br>
* Description: getSign<br>
* CreateDate: 2019/1/30 11:20<br>
*
* @category 对象签名
* @author james.fxy
* @param o
* * @param key
* @return java.lang.String
* @exception Exception
*/
public static String getSign(Object o, String key) throws IllegalAccessException {
ArrayList<String> list = new ArrayList<String>();
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(o) != null && f.get(o) != "") {
String name = f.getName();
XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
if (anno != null)
name = anno.value();
list.add(name + "=" + f.get(o) + "&");
}
}
int size = list.size();
String[] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + key;
log.info("签名数据:" + result);
result = MD5.MD5Encode(result).toUpperCase();
return result;
}
/**
* Title: map格式数据签名<br>
* Description: getSign<br>
* CreateDate: 2019/1/30 11:20<br>
*
* @category map格式数据签名
* @author james.fxy
* @param map
* * @param key
* @return java.lang.String
* @exception Exception
*/
public static String getSign(Map<String, Object> map, String key) {
ArrayList<String> list = new ArrayList<String>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() != "") {
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String[] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + key;
// Util.log("Sign Before MD5:" + result);
result = MD5.MD5Encode(result).toUpperCase();
// Util.log("Sign Result:" + result);
return result;
}
}
将获得的xml格式的支付参数提交到微信后台
/**
* Title: 将成功得到的xml转换为对象<br>
* Description: 将成功得到的xml转换为对象<br>
* CreateDate: 2019/1/31 9:53<br>
*
* @category 将成功得到的xml转换为对象
* @author james.fxy
* @param objectToXml
* @return cn.com.webi.paycenter.wechatminiprogram.param.MiniProgramOrderResultParam
* @exception Exception
*/
private MiniProgramOrderResultParam TranslateSuccessXmlToObject(String objectToXml)
throws Exception {
//支付结果
String result =
HttpClientUtil.doPostByXml(WechatMiniProgramConstant.WECHAT_BACKGROUND_PAYMENT_PLATFORM,
objectToXml, null);
/**
* 时间戳
*/
long currentTime = System.currentTimeMillis();
// 将结果xml格式的数据转换为对象
MiniProgramOrderResultParam miniProgramOrderResultParam =
XstreamUtil.xmlToObject(result,
MiniProgramOrderResultParam.class);
// 通过appid和商户号mch_id获取 key
String key = wechatProgrammerMapper.findKeyByMchId(miniProgramOrderResultParam
.getAppid(), miniProgramOrderResultParam.getMchId());
// 将对象进行加签
// 不对商户号进行加签
miniProgramOrderResultParam.setMchId("");
WechatMiniSignParam wechatMiniSignParam = new WechatMiniSignParam();
wechatMiniSignParam.setAppId(miniProgramOrderResultParam.getAppid());
wechatMiniSignParam.setNonceStr(miniProgramOrderResultParam.getNonceStr());
wechatMiniSignParam.setPackage_("prepay_id=" + miniProgramOrderResultParam.getPrepayId());
wechatMiniSignParam.setTimeStamp(currentTime);
wechatMiniSignParam.setSignType("MD5");
String signResult = Signature.getSign(wechatMiniSignParam, key);
miniProgramOrderResultParam.setSign(signResult);
miniProgramOrderResultParam.setTimeStamp(currentTime);
return miniProgramOrderResultParam;
}
获取到支付结果后返回以下参数:
package cn.com.webi.paycenter.wechatminiprogram.param;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* Title: 微信后台接收到的结果<br>
* Description:微信后台接收到的结果<br>
* Company:</br>
* CreateDate:2019年01月30日 13:36
*
* @author james.fxy
*/
@Data
public class MiniProgramOrderResultParam {
/**
* appid
*/
@ApiModelProperty(value = "appid", example = "wxc0a4fd0811d1eeb0")
private String appid;
/**
* 商户Id
*/
@ApiModelProperty(value = "商户Id", example = "1417321002")
@XStreamAlias("mch_id")
private String mchId;
/**
* 随机字符串
*/
@ApiModelProperty(value = "随机字符串", example = "jWl3rKyMJD15DQqB")
@XStreamAlias("nonce_str")
private String nonceStr;
@ApiModelProperty(value = "签名结果", example = "A1C737C8C961F10D7DE0D7AE8977569D")
private String sign;
/**
* 预支付Id
*/
@ApiModelProperty(value = "预支付ID", example = "wx31114531636844876882bcd42020340563")
@XStreamAlias("prepay_id")
private String prepayId;
/**
* 交易类型
*/
@ApiModelProperty(value = "交易类型", example = "JSAPI")
@XStreamAlias("trade_type")
private String tradeType;
/**
* 返回状态码
*/
@XStreamAlias("return_code")
private String returnCode;
/**
* 返回状态码信息
*/
@XStreamAlias("return_msg")
private String returnMsg;
/**
* 业务结果
*/
@XStreamAlias("result_code")
private String resultCode;
/**
* 微信支付时间戳
*/
private Long timeStamp;
@XStreamAlias("package")
private String package_;
/**
* 支付类型
*/
private String signType;
}
其中还有最重要的一点(小程序调起支付API)
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
这里还需要将支付成功的结果进行加签返回给小程序 加签格式微信后台给出如下:
paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D9B4E54AB1950F51E0649E8810ACD6
获取加签结果的代码如下:
package cn.com.webi.paycenter.wechatminiprogram.param;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
/**
* Title: WechatMiniSignParam<br>
* Description: WechatMiniSignParam<br>
* Company:</br>
* CreateDate:2019年03月05日 17:43
*
* @author james.fxy
*/
@Data
public class WechatMiniSignParam {
private String appId;
private String nonceStr;
@XStreamAlias("package")
private String package_;
private String signType;
private Long timeStamp;
}
/**
* 时间戳
*/
long currentTime = System.currentTimeMillis();
WechatMiniSignParam wechatMiniSignParam = new WechatMiniSignParam();
wechatMiniSignParam.setAppId(miniProgramOrderResultParam.getAppid());
wechatMiniSignParam.setNonceStr(miniProgramOrderResultParam.getNonceStr());
wechatMiniSignParam.setPackage_("prepay_id=" + miniProgramOrderResultParam.getPrepayId());
wechatMiniSignParam.setTimeStamp(currentTime);
wechatMiniSignParam.setSignType("MD5");
String signResult = Signature.getSign(wechatMiniSignParam, key);
miniProgramOrderResultParam.setSign(signResult);
miniProgramOrderResultParam.setTimeStamp(currentTime);
return miniProgramOrderResultParam;