微信开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1
业务流程图如下:
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。
步骤3:统一下单接口返回的正常prepay_id,再按签名规范重新签名生成签名后,将数据传输给APP。参与签名的字段为appid,partnerid,prepayid,noncestr,timestamp,package。注意package格式为Sign=WXPay。
步骤4:商户APP调起微信支付。
步骤5:商户后台接收支付通知。
步骤6:商户后台查询支付结果。
统一下单:(接口连接https://api.mch.weixin.qq.com/pay/unifiedorder)
准备的参数有 :
appid 公众账号ID 微信支付分配的公众账号ID(企业号corpid即为此appId)
mch_id 商户号 微信支付分配的商户号
nonce_str 随机字符串
sign 签名
body 商品描述
out_trade_no 商户订单号 商户系统内部订单号
total_fee 标价金额 订单总金额,单位为分
spbill_create_ip 终端IP
notify_url 通知地址(回调地址)
trade_type 交易类型
一共10个必须参数
步骤1:将必填参数放入Map集合中,生成带sign(生成签名的key是在支付平台设置的)的xml格式的字符串,发送给统一下单链接。
步骤2:将微信返回的xml格式的字符串转换成map格式,并校验sign值是否正确。
步骤3:从map中取出returnCode和resultCode,当两个值都是SUCCESS时,再从map中获取prepay_id预支付交易会话标识,
nonce_str随机字符串,并生成一个时间戳time,将这三个参数用之前生成签名的key再生成一个sign,这个是返回客户端用于拉去订单界面的。
代码如下:
Map orderMap = new Hash();
orderMap.put("appid", WeiXConfig.WEIAPPID);
orderMap.put("mch_id", WeiXConfig.MCH_ID);
orderMap.put("nonce_str", WXPayUtil.generateNonceStr());
orderMap.put("body", WeiXConfig.BODY);
orderMap.put("out_trade_no", model.getId());
Integer payMoney = new BigDecimal(Integer.parseInt(model.getMoney())).multiply(newBigDecimal(100)).intValue(); orderMap.put("total_fee", String.valueOf(payMoney));
orderMap.put("total_fee", String.valueOf(payMoney));
orderMap.put("spbill_create_ip", spbill_create_ip);
orderMap.put("notify_url", WeiXConfig.WEI_NOTIFY_URL);
orderMap.put("trade_type", "APP");
String xmlInfo = WXPayUtil.generateSignedXml(orderMap, WeiXConfig.key);
String weixinPost = HttpsRequestUtil.httpsRequest(uri, "POST", xmlInfo).toString();
Map returnMap = WXPayUtil.xmlToMap(weixinPost);
if (WXPayUtil.isSignatureValid(returnMap, WeiXConfig.key)) {
String returnCode = returnMap.get("return_code");// 通信标识
String resultCode = returnMap.get("result_code");// 交易标识
if (returnCode.equals(WXPayConstants.SUCCESS) &&
resultCode.equals(WXPayConstants.SUCCESS)) {
resultMap.put("code", ConstantUtil.SUCCESS);
resultMap.put("desc", ConstantUtil.SUCCESS_DESC);
String prepayid = returnMap.get("prepay_id");// 预支付交易会话标识
String noncestr = returnMap.get("nonce_str");// 微信返回的随机字符串
long time = WXPayUtil.getCurrentTimestamp();// 时间戳/单位秒
resultMap.put("prepay_id", prepayid);
resultMap.put("nonce_str", noncestr);
resultMap.put("mch_id", WeiXConfig.MCH_ID);
resultMap.put("timestamp", time);
String sign = secondSign(noncestr, time, prepayid, WeiXConfig.key);
resultMap.put("sign", sign);
} else {
resultMap.put("code", ConstantUtil.SYS_ERROR);
resultMap.put("desc", returnMap.get("return_msg"));
}
} else {
}
二次签名代码:
/**
* 二次签名
*
* @param map
* @param key
* @return
*/
public String secondSign(String nonce_str, long time, String prepayid, String key) {
String sign = "";
try {
Map map = new HashMap<>();
map.put("appid", WeiXConfig.WEIAPPID);
map.put("partnerid", WeiXConfig.MCH_ID);
map.put("package", "Sign=WXPay");
map.put("noncestr", nonce_str);
map.put("timestamp", String.valueOf(time));
map.put("prepayid", prepayid);
String xmlStr = WXPayUtil.generateSignedXml(map, key);
LogUtil.info("key-->{}", key);
Map xmlMap = WXPayUtil.xmlToMap(xmlStr);
LogUtil.info("xmlMap-->{}", xmlMap);
sign = xmlMap.get("sign");
} catch (Exception e) {
LogUtil.info("Exception-->{}", e);
}
return sign;
}
到此统一下单结束
查询订单:(链接https://api.mch.weixin.qq.com/pay/orderquery)
需要参数
appid 公众账号ID
mch_id 商户号
out_trade_no 商户订单号
nonce_str 随机字符串
sign 签名
一共5个参数
步骤1:将必填参数放入Map集合中,生成带sign(生成签名的key是在支付平台设置的)的xml格式的字符串,发送给查询订单链接。
步骤2:将微信返回的xml格式的字符串转换成map格式,并校验sign值是否正确。
步骤3:从map中取出appid和mch_id和result_code,当appid、商户mch_id和服务器值相同,并且result_code等于SUCCESS时,取map中trade_state(订单状态),根据订单状态决定是否下一步,订单状态有以下可能:
SUCCESS—支付成功
REFUND—转入退款
NOTPAY—未支付
CLOSED—已关闭
REVOKED—已撤销(刷卡支付)
USERPAYING--用户支付中
PAYERROR--支付失败(其他原因,如银行返回失败)
当订单状态是成功时,核对订单号以及金额是否正确,之后根据订单状态给用户发道具;到此查询订单结束,代码如下:
Map orderMap = new Hash();
orderMap.put("appid", WeiXConfig.WEIAPPID);
orderMap.put("mch_id", WeiXConfig.MCH_ID);
orderMap.put("out_trade_no", id);
String nonce_str = WXPayUtil.generateNonceStr();
orderMap.put("nonce_str", nonce_str);
String xmlInfo = WXPayUtil.generateSignedXml(orderMap, WeiXConfig.key);
String weixinPost = HttpsRequestUtil.httpsRequest(uri, "POST", xmlInfo).toString();
Map returnMap = WXPayUtil.xmlToMap(weixinPost);
String return_code = returnMap.get("return_code");
if (WXPayConstants.SUCCESS.equals(return_code)) {
String appid = returnMap.get("appid");
String mch_id = returnMap.get("mch_id");
String result_code = returnMap.get("result_code");
/* 验签 */
if (WXPayUtil.isSignatureValid(returnMap, WeiXConfig.key)) {
if (WeiXConfig.WEIAPPID.equals(appid) && WeiXConfig.MCH_ID.equals(mch_id)
&& "SUCCESS".equals(result_code)) {
String trade_state = returnMap.get("trade_state");// 交易状态
String total_fee = returnMap.get("total_fee");// 总金额
String out_trade_no = returnMap.get("out_trade_no");// 商户订单号
String trade_state_desc = returnMap.get("trade_state_desc");// 交易状态描述
/* 状态成功 金额正确 */
if ("SUCCESS".equals(trade_state) && total_fee.equals(String.valueOf(Integer.parseInt(model.getMoney()) * 100))) {
/* 订单号相同 */
if (out_trade_no.equals(id)) {
}
}
}
}
}
回调结果(连接是自己填的notify_url)
代码如下:
BufferedReader reader = null;
LogUtil.info(System.currentTimeMillis() + "<收到支付回调>");
reader = request.getReader();
String line = "";
String xmlString = null;
StringBuffer inputString = new StringBuffer();
while ((line = reader.readLine()) != null) {
inputString.append(line);
}
xmlString = inputString.toString();
request.getReader().close();
if ("".equals(xmlString)) {
return "";
}
Map map = WXPayUtil.xmlToMap(xmlString);
LogUtil.info("----接收到的数据如下:---" + map.toString());
String out_trade_no = map.get("out_trade_no");
boolean flag = false;
String test = Configuration.getProperty(Configuration.TESTINSTANCE);
/* 不是测试 */
if (null == test || "".equals(test)) {
flag = checkWeiPayResult(map);
} else {
switch (test) {
case "2":
flag = checkWeiPayResult2(map);
break;
default:
flag = checkWeiPayResult1(map);// 默认
break;
}
}
/* 检验签名 */
if (flag) {
Jedis jedis = JedisUtil.getJedis();
try {
Map payMap = new HashMap<>();
payMap.put("id", out_trade_no);
PayOrderVerify entity = payService.queryOrderById(payMap);
if (!entity.getCode().equals(PayConfig.CODE_FINISH) && entity.getStatus().equals(PayConfig.SEND_STATUS_NOT)) {
entity.setCode(PayConfig.CODE_FINISH);
payService.updateOrder(PayUtil.convert(entity));
LogUtil.info("订单号验证成功,订单号:" + out_trade_no + ",玩家id:" + entity.getUserId());
/* 通知大厅发货 */
int command = ConstantUtil.G_CONST_CG_PS_PAY_CALLBACK_COMMAND_R;
sendGoods(command, entity.getUserId(), entity.getpId(), entity.getId());
}
jedis.del("order" + out_trade_no);
} catch (Exception e) {
LogUtil.info("Exception-->{}", e);
} finally {
JedisUtil.returnResource(jedis);
}
}
return returnXML(map.get("return_code"));
}
参数校验代码:
/* 校验微信支付回调结果 正式的 */
public boolean checkWeiPayResult(Map map) {
String return_code = map.get("return_code");
try {
if ("SUCCESS".equals(return_code)) {
String mch_id = map.get("mch_id");
if (WeiXConfig.MCH_ID.equals(mch_id)) {
if (WXPayUtil.isSignatureValid(map, WeiXConfig.key)) {
String fee_type = map.get("fee_type");
if ("CNY".equals(fee_type)) {
String out_trade_no = map.get("out_trade_no");
Map payMap = new HashMap<>();
payMap.put("id", out_trade_no);
PayOrderVerify entity = payService.queryOrderById(payMap);
if (null != entity) {
String result_code = map.get("result_code");
if ("SUCCESS".equals(result_code)) {
int total_fee = Integer.parseInt(map.get("total_fee"));
if (Integer.parseInt(entity.getMoney()) * 100 == total_fee) {
return true;
} else {
LogUtil.info("总金额不对,错的total_fee-->{},正确的total_fee-->{}", total_fee,
Integer.parseInt(entity.getMoney()) * 100);
}
} else {
LogUtil.info("result_code-->{}", result_code);
}
} else {
LogUtil.info("订单号不对,out_trade_no-->{}", out_trade_no);
}
} else {
LogUtil.info("货币种类不同,fee_type-->{}", fee_type);
}
} else {
LogUtil.info("签名验证失败,sign-->{}", map.get("sign"));
}
} else {
LogUtil.info("商户ID不一致,mch_id-->{}", mch_id);
}
} else {
LogUtil.info("return_code-->{}", return_code);
}
} catch (Exception e) {
LogUtil.info("Exception-->{}", e);
}
return false;
}
到此全部结束!!!