微信支付开发api文档
首先一个最简单的支付功能,大体分为三步
统一下单
回调处理支付结果
这里用到了github上的一个依赖,相当于支付的工具包
<dependency>
<groupId>com.github.javen205groupId>
<artifactId>IJPayartifactId>
<version>1.1.3version>
dependency>
这边从前端小程序传过来的参数有:
orderId
– 我们系统这边的订单id
openId
– 小程序传过来的用户标识
我们配置文件中配置的固定参数有:
appid
– 小程序id
mchId
– 商户号
notify_url
– 支付成功以后回调的我们写的接口地址
partnerKey
– 商户平台设置的密钥key
/**
* $.ajax后需要接受的JSON
*
* @author
*
*/
public class AjaxJson implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private boolean success = true;// 是否成功
private String msg = "操作成功";// 提示信息
private Object obj = null;// 其他信息
private ConcurrentMap attributes;// 其他参数
private String errorCode;// 错误码
private Integer totalSize;// 错误码
getter...setter方法
}
/**
* 小程序微信支付的第一步,统一下单
* @return
* @author zgd
* @time 2018年7月9日17:53:35
*/
@RequestMapping("/createUnifiedOrder")
@ResponseBody
public AjaxJson createUnifiedOrder(HttpServletRequest request) {
AjaxJson aj = new AjaxJson();
aj.setSuccess(false);
String orderId = request.getParameter("orderId");
//接受参数(openid)
String openid = request.getParameter("openid");
if (StringUtils.isAnyBlank(orderId,openid)){
aj.setMsg("支付失败,支付所需参数缺失");
return aj;
}
//这里调用service层根据订单id获取订单数据,这里省略不表
Map<String, String> mapBasic = getOrderInfoById(orderId);
if (mapBasic == null) {
aj.setMsg("支付失败,暂时无法获取到您的订单数据,请稍后再试");
return aj;
}
String return_msg = "统一订单失败";
try {
//支付金额 **金额不能有小数点,单位是分!!**
BigDecimal price = new BigDecimal(mapBasic.get("payAmount").toString());
BigDecimal beishu = new BigDecimal("100");
BigDecimal priceFee=price.multiply(beishu);
//商家订单号
String orderNo = mapBasic.get("orderNo").toString();
//创建 时间戳
String timeStamp = Long.valueOf(System.currentTimeMillis()).toString();
//生成32位随机数
UUID uuid = UUID.randomUUID();
String nonceStr = uuid.toString().replaceAll("-","");
//商品描述
String body = "XX商城-支付订单";
//创建hashmap(用户获得签名)
SortedMap<String, String> paraMap = new TreeMap<String, String>();
//设置请求参数(小程序ID)
paraMap.put("appid", appid);
//设置请求参数(商户号)
paraMap.put("mch_id", mchId);
//设置请求参数(随机字符串)
paraMap.put("nonce_str", nonceStr);
//设置请求参数(商品描述)
paraMap.put("body", body);
//设置请求参数(商户订单号)
paraMap.put("out_trade_no", orderNo);
//设置请求参数(总金额)
paraMap.put("total_fee", priceFee.toBigInteger().toString());
//设置请求参数(终端IP) 如果是springmvc,或者能获取到request的servlet,用下面这种
paraMap.put("spbill_create_ip", request.getRemoteAddr());
//设置请求参数(通知地址)
paraMap.put("notify_url",notify_url);
//设置请求参数(交易类型)
paraMap.put("trade_type", String.valueOf(WxPayApi.TradeType.JSAPI));
//设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
paraMap.put("openid", openid);
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
String sign = PaymentKit.createSign(paraMap, partnerKey);
paraMap.put("sign", sign);
//统一下单,向微信api发送数据
logger.info("微信小程序统一下单发送的数据: "+paraMap.toString());
String xmlResult = WxPayApi.pushOrder(false, paraMap);
logger.info("微信小程序统一下单接受返回的结果: "+xmlResult);
//转成xml
Map<String, String> map = PaymentKit.xmlToMap(xmlResult);
//返回状态码
String return_code = (String) map.get("return_code");
return_msg = return_msg+", "+ (String) map.get("return_msg");
//返回给小程序端需要的参数
Map<String, String> returnMap = new HashMap<String, String>();
if(Constants.SUCCESS.equals(return_code)){
//返回的预付单信息
returnMap.put("appId",appid);
returnMap.put("nonceStr", nonceStr);
String prepay_id = (String) map.get("prepay_id");
returnMap.put("package", "prepay_id=" + prepay_id);
returnMap.put("signType","MD5");
//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
returnMap.put("timeStamp", timeStamp);
//拼接签名需要的参数
//再次签名,这个签名用于小程序端调用wx.requesetPayment方法
String paySign = PaymentKit.createSign(returnMap, partnerKey).toUpperCase();
returnMap.put("paySign", paySign);
aj.setObj(returnMap);
aj.setMsg("操作成功");
aj.setSuccess(true);
return aj;
}else{
aj.setMsg(getMsgByCode(return_code));
logger.error(Thread.currentThread().getStackTrace()[1].getMethodName()+">>>"+return_msg);
}
} catch (Exception e) {
logger.error(Thread.currentThread().getStackTrace()[1].getMethodName() +"发生的异常是: ",e);
aj.setMsg("微信支付下单失败,请稍后再试");
}
return aj;
}
getMsgByCode
/**
* 判断返回的return_code,给前端相应的提示
* @param return_code
* @return
* @author zgd
* @time 2018年7月9日17:53:13
*/
private String getMsgByCode(String return_code) {
switch (return_code){
case "NOTENOUGH":return "您的账户余额不足";
case "ORDERPAID":return "该订单已支付完成,请勿重复支付";
case "ORDERCLOSED":return "当前订单已关闭,请重新下单";
case "SYSTEMERROR":return "系统超时,请重新支付";
case "OUT_TRADE_NO_USED":return "请勿重复提交该订单";
default:return "网络正在开小差,请稍后再试";
}
}
比如上面配置的notify_url是http://系统的ip和端口/wxPay/notify
@RequestMapping("/wxPay")
public class WxPayController{
private String paternerKey = "XXXXXX";
@RequestMapping("/notify")
public void notify(HttpServletRequest request){
//获取所有的参数
StringBuffer sbf=new StringBuffer();
// 支付结果通用通知文档: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
String xmlMsg = HttpKit.readData(request);
System.out.println("支付通知="+xmlMsg);
Map params = PaymentKit.xmlToMap(xmlMsg);
String result_code = params.get("result_code");
//校验返回来的支付结果,根据已经配置的密钥
if(PaymentKit.verifyNotify(params, paternerKey)){
//Constants.SUCCESS="SUCCESS"
if ((Constants.SUCCESS).equals(result_code)) {
校验通过. 更改订单状态为已支付, 修改库存
}
}
}
}
拼接参数为字符串的工具方法
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder ret;
br = request.getReader();
String line = br.readLine();
if (line != null) {
ret = new StringBuilder();
ret.append(line);
} else {
return "";
}
while ((line = br.readLine()) != null) {
ret.append('\n').append(line);
}
return ret.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
finally {
if (br != null) {
try {br.close();} catch (IOException e) {LogKit.error(e.getMessage(), e);}
}
}
}