APP微信支付不同于扫码支付,我做java后端和APP交互,客户端直接调取微信支付接口,不需要我们生成二维码进行扫码支付.
但是需要java后端统一下单.
APP支付流程大致分为3大步.
1,统一下单
a,应用场景
商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再在APP里面调起支付。
b, 接口链接
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
c, 是否需要证书
不需要
这个是必须要传入参数的有
应用ID appid (这个是微信账户id)
商户号 mch_id (商户号)
随机字符串 nonce_str (用于生成签名中用)
签名 sign
商品描述 body
商户订单号 out_trade_no
总金额 total_fee
终端IP spbill_create_ip
通知地址 notify_url
交易类型 trade_type
@Service
@Transactional
public class ChatPayServiceImpl implements ChatPayService {
private static final Logger log = LoggerFactory.getLogger("adminLogger");
public static final String SUCCESS_CODE = "200";
public static final String SUCCESS_MSG = "支付成功";
public static final String EMPTY_CODE = "401";
public static final String EMPTY_MSG = "订单为空";
public static final String ERROR_CODE = "400";
public static final String ERROR_MSG = "系统错误";
@Autowired
private ProOrderDao orderMapper;
@Autowired
private PayInfoService payInfoService;
@Autowired
private ProOrderDao proOrderDao;
@Override
public Map<String, Object> goWeChatPay(String orderId, HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
if(EmptyUtil.isEmpty(orderId)){
map.put("code", EMPTY_CODE);
map.put("msg", EMPTY_MSG);
return map;
}
//获取订单信息
ProOrder payParameter = orderMapper.getByOrderId(orderId);
long prices = payParameter.getPaidMoney();
// double price = prices.doubleValue();
System.out.println("price:" + prices);
// 微信开放平台审核通过的应用APPID
System.out.println("appid是:" + WxpayConfig.appid);
System.out.println("mch_id是:" + WxpayConfig.mch_id);
String nonce_str = RandomStrUtil.getRandomString(30);
System.out.println("随机字符串是:" + nonce_str);
String total_price = null;// 订单总金额,单位为分,详见支付金额
String spbill_create_ip = WXSignUtils.getRemortIP(request);// "127.0.0.1";
String notify_url = WxpayConfig.notify_url;
String trade_type = "APP";
// 参数:开始生成签名
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
parameters.put("appid", WxpayConfig.appid);//微信平台id应用ID
parameters.put("body", payParameter.getBf1());//商品描述
parameters.put("mch_id", WxpayConfig.mch_id);//商户号
parameters.put("nonce_str", nonce_str);//随机字符串
parameters.put("notify_url", notify_url);//通知地址及回调地址
parameters.put("out_trade_no", String.valueOf(payParameter.getOrderNum()));//商户订单号
parameters.put("spbill_create_ip",spbill_create_ip);//终端IP
parameters.put("total_fee", 1);//总金额 测试暂时用一分
parameters.put("trade_type", trade_type);//交易类型
String sign = WXSignUtils.createSign("UTF-8", parameters);
System.out.println("第一次签名是:" + sign);
Unifiedorder unifiedorder = new Unifiedorder();
unifiedorder.setAppid(WxpayConfig.appid);//微信平台id应用ID
unifiedorder.setBody(payParameter.getBf1());//商品描述
unifiedorder.setMch_id(WxpayConfig.mch_id);//商户号
unifiedorder.setNonce_str(nonce_str);//随机字符串
unifiedorder.setNotify_url(notify_url);//通知地址及回调地址
unifiedorder.setOut_trade_no(String.valueOf(payParameter.getOrderNum()));//商户订单号
unifiedorder.setSpbill_create_ip(spbill_create_ip);//终端IP
unifiedorder.setTotal_fee("1");//单位 分
unifiedorder.setTrade_type(trade_type);//交易类型
unifiedorder.setSign(sign);//第一次签名
// 构造xml参数 , 向微信发送内容都是xml形式的,所以要生成xml格式
String xmlInfo = HttpXmlUtils.xmlInfo(unifiedorder);
System.out.println("xmlInfo:" + xmlInfo);
String wxUrl = WxpayConfig.url;
String method = "POST";
String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString();// 请求微信
System.out.println("weixinPost:" + weixinPost);
UnifiedorderResult unifiedorderResult = ParseXMLUtils.jdomParseXml(weixinPost);// 解析微信的反馈
if (unifiedorderResult != null){
if ("SUCCESS".equals(unifiedorderResult.getReturn_code())){
if("INVALID_REQUEST".equals(unifiedorderResult.getErr_code())){
map.put("code", ERROR_CODE);
map.put("msg", "参数错误");
return map;
}
//二次签名
SortedMap<Object, Object> parameters1 = new TreeMap<Object, Object>();
parameters1.put("appid", WxpayConfig.appid);//微信平台id应用ID
parameters1.put("partnerid", WxpayConfig.mch_id);//商户号
parameters1.put("prepayid",ParseXMLUtils.jdomParseXml(weixinPost).getPrepay_id());//第一次签名
parameters1.put("noncestr", ParseXMLUtils.jdomParseXml(weixinPost).getNonce_str());//随机字符串
parameters1.put("timestamp", String.valueOf(new Date().getTime()).substring(0, 10));
parameters1.put("package",WxpayConfig.wx_package);
String sign1 = WXSignUtils.createSign("UTF-8", parameters1);
System.out.println("第二次签名是:" + sign1);
// 开始拼接App调起微信的参数
SortedMap<Object, Object> wxAppparameters = new TreeMap<Object, Object>();
wxAppparameters.put("appId", unifiedorderResult.getAppid());
wxAppparameters.put("partnerId", unifiedorderResult.getMch_id());
wxAppparameters.put("prepayId", unifiedorderResult.getPrepay_id());
wxAppparameters.put("appPackage", WxpayConfig.wx_package);//扩展字段
wxAppparameters.put("nonceStr", ParseXMLUtils.jdomParseXml(weixinPost).getNonce_str());
wxAppparameters.put("timeStamp", String.valueOf(new Date().getTime()).substring(0, 10));
wxAppparameters.put("sign", sign1);
map.put("code", SUCCESS_CODE);
map.put("msg", SUCCESS_MSG);
map.put("data", wxAppparameters);
return map;
} else {
System.out.println("错误原因为:" + unifiedorderResult.getReturn_msg());
map.put("code", ERROR_CODE);
map.put("msg", unifiedorderResult.getReturn_msg());
return map;
}
} else {
System.out.println("服务端请求微信的返回值异常。");
map.put("code", ERROR_CODE);
map.put("msg", "服务端请求微信的返回值异常。");
return map;
}
}
/**
* @Function: 微信回调接口
* @author:
* @Date:
*/
@Override
@Transactional(rollbackFor=Exception.class)
public String weChatNotify(HttpServletRequest request) throws Exception {
Map<String, String> map = new HashMap<>();
System.out.println("----------------微信回调开始啦----------------------");
// 读取参数
InputStream inputStream;
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
inputStream.close();
// 解析xml成map
Map<String, String> m = new HashMap<String, String>();
m = WXSignUtils.doXMLParse(sb.toString());
System.out.println(m);
// 过滤空 设置 TreeMap
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
Iterator<String> it = m.keySet().iterator();
while (it.hasNext()){
String parameter = it.next();
String parameterValue = m.get(parameter);
String v = "";
if (null != parameterValue){
v = parameterValue.trim();
}
System.out.println("p:" + parameter + ",v:" + v);
packageParams.put(parameter, v);
}
// 微信支付的API密钥
String key = WxpayConfig.key;
if(!isTenpaySign("UTF-8", packageParams, key)){
map.put("return_code", "FAIL");
map.put("return_msg", "return_code不正确");
return StrUtil.GetMapToXML(map);
}
//返回状态存入redis中
if(m.get("return_code").equals("SUCCESS")){
RedisUtil.set("wx"+m.get("out_trade_no"),m.get("result_code"),300);
}
if (isTenpaySign("UTF-8", packageParams, key)){
// 验证通过
if ("SUCCESS".equals((String) packageParams.get("return_code"))){
String out_trade_no = (String) packageParams.get("out_trade_no");
/* 订单不为空 */
if (!EmptyUtil.isEmpty(out_trade_no)){
//支付成功后的业务处理 更新订单状态 插入支付记录
ProOrder order = proOrderDao.getByOrderNum(out_trade_no);
String orderId = order.getOrderId();
int i = proOrderDao.update1("20", orderId);//修改订单状态
//插入支付信息数据(支付信息表)
PayInfo payInfo=new PayInfo();
payInfo.setPayId(OrderNumGenerateUtil.getUUID());
payInfo.setUserId(order.getUserId());//用户id
payInfo.setOrderNum(order.getOrderNum());//订单号
payInfo.setPayPlatform("2");//微信支付
payInfo.setStatus("1");//支付成功
payInfo.setFlowNo((String) packageParams.get("flow_no"));//支付流水账号
payInfo.setCreateTime(new Date());
payInfo.setUpdateTime(new Date());
JsonResult save = payInfoService.save(payInfo);//保存支付信息
map.put("return_code", "SUCCESS");
map.put("return_msg", save.toString());
return StrUtil.GetMapToXML(map);
}
}
} else {
System.out.println("支付失败");
map.put("return_code", "error");
map.put("return_msg", "支付失败");
return StrUtil.GetMapToXML(map);
}
System.out.println("支付失败");
System.out.println("支付失败");
map.put("return_code", "error");
map.put("return_msg", "支付失败");
return StrUtil.GetMapToXML(map);
}
WXSignUtils是生成签名的工具类
以下是工具类的写法
/**
* @Description: 微信支付签名
* @author: Administrator
* @date:
*/
public class WXSignUtils {
/* API秘钥 */
private static String Key ="d9f5df2e7e187672ba112623dec18478";
/**
* 微信支付签名算法sign
* @param characterEncoding
* @param parameters
* @return
*/
public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + Key);
System.out.println("字符串拼接后是:"+sb.toString());
String sign = MD5Util.encode(sb.toString()).toUpperCase();
return sign;
}
public static String getSign(String characterEncoding,Map<String, String> data){
StringBuffer sb = new StringBuffer();
Set es = data.entrySet();//所有参与传参的参数按照accsii排序(升序)
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + Key);
System.out.println("字符串拼接后是:"+sb.toString());
String sign = MD5Util.encode(sb.toString()).toUpperCase();
return sign;
}
/**
* @Function: 获取IP
* @author:
* @Date:
*/
public static String getRemortIP(HttpServletRequest request) {
if (request.getHeader("x-forwarded-for") == null) {
return request.getRemoteAddr();
}
return request.getHeader("x-forwarded-for");
}
/**
* @Function: 解析XML
* @author:
* @Date:
*/
public static Map doXMLParse(String strxml) throws JDOMException,
IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
org.jdom.Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
org.jdom.Element e = (org.jdom.Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
// 关闭流
in.close();
return m;
}
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
org.jdom.Element e = (org.jdom.Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
}
这个代码中其中要用到两次签名,去支付的 模块中,生成第一个签名,和必须要输入的参数请求微信 HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo)
这是请求微信是的xml格式,当然,微信返回给我们的,也是xml形式的,所以获取的时候也要对结果进行解析.
<xml>
<appid>wx2421b1c4370ec43b</appid>`在这里插入代码片`
<body>APP支付测试</body>
<mch_id>10000100</mch_id>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>APP</trade_type>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>
2,调起支付接口
解析完以后,我们获取一个
预支付交易会话标识 prepay_id
SortedMap<Object, Object> wxAppparameters = new TreeMap<Object, Object>();
wxAppparameters.put(“appId”, unifiedorderResult.getAppid());
wxAppparameters.put(“partnerId”, unifiedorderResult.getMch_id());
wxAppparameters.put(“prepayId”, unifiedorderResult.getPrepay_id());//这个就是生成的会话标识
wxAppparameters.put(“appPackage”, WxpayConfig.wx_package);//扩展字段
wxAppparameters.put(“nonceStr”, ParseXMLUtils.jdomParseXml(weixinPost).getNonce_str());
wxAppparameters.put(“timeStamp”, String.valueOf(new Date().getTime()).substring(0, 10));
wxAppparameters.put(“sign”, sign1);
其中这里面的顺序和参数不能更改,
其中wxAppparameters.put(“sign”, sign1)这里的sign1是第二次生成的签名,第二次生成的签名要用的字符串要和第一次的字符串一样,要不然调不了支付接口.(这是一个小坑)
调取支付接口要用的参数分别有
应用ID | appid |
商户号 | partnerid |
预支付交易会话ID | prepayid |
扩展字段 | package |
随机字符串 | noncestr |
时间戳 | timestamp |
签名 | sign |
商户号 |
(详见APP支付接口https://pay.weixin.qq.com/wiki/doc/api)
传入app接口就可以,APP端接入微信支付接口跳转支付页面,支付成功以后,
微信调取回调接口.你写的notify_url回调地址,
因为我的这个项目java后端还要用成功之后微信给的回调地址中的信息.
支付信息表,修改支付状态等等.
但是在回调地址中不是写你IP地址.微信就能识别你的IP的.
(在局域网是无法进行回调的,必须将你的服务端放在公网上进行测试,)
3,支付结果通知
通过结果通知截取你想要的信息去增删改数据信息.
写的有点乱.写的不对的地方敬请指正.
欢迎大家一起来探讨.