app用户微信支付-Java后端
以下是微信官方的交互时序图,统一下单API、支付结果通知API和查询订单API等都涉及签名过程,调用都必须在商户服务器端完成。
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。
步骤5:商户后台接收支付通知。
下面来看微信接口
一、设置必要参数
public static final String PATERNERKEY = "";//商家的密钥
public static final String APP_ID = "";//APPID
public static final String MINI_ID = "";//小程序ID
public static final String MCH_ID = "";//商户号
public static final String FEE_TYPE = "CNY";//货币类型,人民币
private String NOTIFY_URL = "";//程序后台地址
public static final String TRADE_TYPE = "APP";//支付类型(APP)
public static final String JSAPI_TRADE_TYPE = "JSAPI";//支付类型(小程序)
二、拉取微信预付单
public Map wechatPay(Map inDTO){
Map outDTO=new HashMap();//创建返回对象
//.......省略参数校验
/*主要参数有:
* total_fee 金额,传入单位(元)
* userId 程序用户ID,用于记录订单
* buyType 购买类型 ,可不用
* buyGoodsId 商品ID,自定义
* body 商品描述
* attach 附加数据
*/
// 1.0 拼凑需要的参数
String appid = APP_ID; // 应用ID
String mch_id = MCH_ID; // 商户号
String nonce_str = WXPayUtil.generateNonceStr(); // 生成随机数
String body = inDTO.get("body") == null ? "":inDTO.get("body").toString();//商品描述
String attach = inDTO.get("attach") == null ? "":inDTO.get("attach").toString();//附加数据
String out_trade_no = WXPayUtil.generateNonceStr(); // 生成商户订单号
String fee_type = FEE_TYPE;//货币类型
Double totalFee=Double.parseDouble(inDTO.get("total_fee").toString());
Long total_Fee= new Double(totalFee * 100.0).longValue();
String total_fee = String.valueOf(total_Fee) ;//总金额(分)
String spbill_create_ip = "127.0.0.1"; //终端IP
String notify_url = NOTIFY_URL+"/wxNotify";//通知地址,用于接收微信支付回调
String trade_type = TRADE_TYPE; //交易类型
//签名 sign 后面追加
// 2.0 生成map集合
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", appid); // 应用ID
packageParams.put("mch_id", mch_id); // 商户号
packageParams.put("attach", attach); // 商户号
packageParams.put("nonce_str", nonce_str); // 生成随机数
packageParams.put("out_trade_no", out_trade_no); // 生成商户订单号
packageParams.put("total_fee", total_fee); // 总金额 (分)
packageParams.put("body", body); // 商品描述
packageParams.put("fee_type", fee_type); // 货币类型
packageParams.put("notify_url", notify_url); // 通知地址
packageParams.put("trade_type", trade_type); // 交易类型
packageParams.put("spbill_create_ip", spbill_create_ip); // 终端IP
Map<String, String> returnMap = new HashMap<>();
try {
// 3.0 利用上面的参数,先去生成自己的签名
String sign = WXPayUtil.createSign(packageParams,PATERNERKEY);
// String sign = WXPayUtil.createSign(packageParams,getSignkey());
// 4.0 将签名再放回map中,它也是一个参数
packageParams.put("sign", sign);
// 5.0将当前的map结合转化成xml格式
String xml = WXPayUtil.mapToXml(packageParams);
logger.info(xml);
// 6.0获取需要发送的url地址
String wxUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // 获取微信统一下单的api接口
//String wxUrl = "https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder"; // 微信沙盒测试模式,金额固定为 101分
// 7,向微信发送请求
String returnXml = CertHttpUtil.postData(wxUrl, xml);
// 8,将微信返回的xml结果转成map格式
returnMap = WXPayUtil.xmlToMap(returnXml);
if("SUCCESS".equals(returnMap.get("return_code"))){
//再次加密sign,为确保加密一致,故后端加密后返回前端
long timestamp= WXPayUtil.getCurrentTimestamp();//时间戳
String noncestr = WXPayUtil.generateNonceStr();//随机字符串
String sign1= getSign(returnMap.get("prepay_id"),String.valueOf(timestamp),noncestr);
returnMap.put("nonce_str",noncestr);
returnMap.put("sign",sign1);
returnMap.put("timestamp",String.valueOf(timestamp));
outDTO.put("returnMap",returnMap);//返回调取微信APP所需的参数
outDTO.put("out_trade_no",out_trade_no);//上面生成的商户订单号
outDTO.put("res","success")
/*这里可以先创建一条订单信息,
*01:已支付;02:已取消;03:待支付。
*支付类型暂定为03:待支付,因为用户取消支付,后端不会收到回调数据,
*所以暂定为待支付,等到收到回调数据后再修改支付状态。
*/
}else{
logger.error("获取prepay_id失败");
//定义返回信息
outDTO.put("res","获取prepay_id失败")
}
}catch (Exception e){
//省略
}
return outDTO;
三、商户APP调起微信支付,此为前端操作,后端不做说明
四、商户后端接收微信支付通知
@RequestMapping("/wxNotify")
@ResponseBody
public void wxNotify(HttpServletRequest request, HttpServletResponse response){
try {
String returnXml = reciverWx(request); // 接收到异步的参数
logger.info("***********************微信异步通知**************************");
logger.info(returnXml);
Map<String, String> returnMap = WXPayUtil.xmlToMap(returnXml);// 解析xml成map
// 过滤空 设置 TreeMap
SortedMap<String, String> packageParams = new TreeMap<String, String>();
Iterator it = returnMap.keySet().iterator();
while (it.hasNext())
{
String parameter = (String) it.next();
String parameterValue = returnMap.get(parameter);
String v = "";
if (null != parameterValue)
{
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
// 判断签名是否正确
String resXml = "";
if (WXPayUtil.isSignatureValid(packageParams,PATERNERKEY)) {
if ("SUCCESS".equals(packageParams.get("return_code"))) {
// 返回成功,获取订单号,获取金额
String out_trade_no = packageParams.get("out_trade_no"); // 商户订单号
String total_fee = packageParams.get("total_fee");
//再获取系统中储存的订单信息
/*
*查询系统中储存的订单信息,略
*/
//进行比对
if(订单号一致&&金额一致){
/*
*这里写支付成功后的业务代码,别忘了把前面设置的待支付状态,改为已支付!!
*/
logger.info("***********************微信异步通知-成功**************************");
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
}else{
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[参数错误]]></return_msg>" + "</xml> ";
}
} else {// 如果微信返回支付失败,将错误信息返回给微信
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[交易失败]]></return_msg>" + "</xml> ";
}
}else{
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> ";
}
// 处理业务完毕,将业务结果通知给微信
// ------------------------------
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}catch (Exception e){
e.getMessage();
}
}
/**
* 接收微信的异步通知
*
* @throws IOException
*/
public static String reciverWx(HttpServletRequest request) throws IOException
{
InputStream inputStream;
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
while ((s = in.readLine()) != null)
{
sb.append(s);
}
in.close();
inputStream.close();
return sb.toString();
}
注:代码中所用到的工具类(WXPayUtil),均为微信原生代码。微信官方API文档---->SDK和DEMO下载