APP支付:
1.微信支付:
这是app支付时,一个完整的流程
1.1首先要去微信开放平台注册,并创建APP
1.2取得微信支付的权限
1.3 商户平台有公众号平台和APP平台两种,一定要是APP平台,可以在下面这个地方查看
1.4 我们需要在商户平台和开放平台上获取到以下数据:
开放平台:
APPID、APP_SECRET:见1图。
商户平台:
秘钥(APP_KEY):微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
商户号(MCH_ID):微信商户平台(pay.weixin.qq.com)-->账户设置-->商户信息-->微信支付商户号
数字证书(微信退款需要使用):微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->证书下载
2.支付宝的操作流程简单,文档清楚(略)
3准备工作结束,我的产品是使用Hbuilder开发的HTML5的APP,后端是依托于Java Web程序。工作流程参照时序图。主要代码如下,详情见文件
其中用到一些工具方法 或者客户端创建代码都在链接里面有,我放在了码云上面 https://gitee.com/muziTM/payDemo
3.1 入口
/** * 支付类 包含支付宝和微信 * @author Tianming_Li * */ @Controller @RequestMapping("/payController") public class PayController { //订单 @Autowired OrdersService orderService; //付费 @Autowired PayService payService; //退费 @Autowired RefundService refundService; //支付成功回调 @Autowired CallBackService callBackService; /** * 下单 * @param outTradeNo * @param payType * @param request * @param response * @param modelMap * @return */ @RequestMapping("/pay") public String pay(@RequestParam(name="outTradeNo") String outTradeNo,@RequestParam(name="payType") String payType, HttpServletRequest request,HttpServletResponse response, ModelMap modelMap){ //根据订单号获取到订单信息 Orders order = orderService.selectOrdersInfoByOutTradeNo(outTradeNo); if("wxpay".equals(payType)){ return payService.wxPay(order, request, response, modelMap); }else{ return payService.aliPay(order); } } /** * 退款 * @param outTradeNo * @param payType * @param request * @param response * @return */ @RequestMapping("/refunds") public String refunds(@RequestParam(name="outTradeNo") String outTradeNo,@RequestParam(name="payType") String payType, HttpServletRequest request,HttpServletResponse response){ //根据订单号获取到订单信息 Orders order = orderService.selectOrdersInfoByOutTradeNo(outTradeNo); if("wxpay".equals(payType)){ return refundService.wxRefund(order, request, response); }else{ return refundService.aliRefund(order); } } /** * 接收微信支付成功通知 * * @param request * @param response * @throws IOException */ @RequestMapping(value = "/getWxPayNotify") public void getWxPayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException { callBackService.wxCallBack(request, response); } /** * 接收支付宝支付成功回调 * @param request * @param response * @throws IOException */ @RequestMapping("/getAliPayNotify") public void getAliPayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException { callBackService.aliCallBack(request, response); } }
3.2 支付代码
/** * 支付处理 * @author Tianming_Li * */ @Service("payService") public class PayServiceImpl implements PayService { protected static final Log logger = LogFactory.getLog(PayServiceImpl.class); @Override public String wxPay(Orders order, HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) { BaseDao dao = DaoUtils.getDao("sys"); StringBuilder sb = new StringBuilder(); sb.append("SELECT * FROM WX_PAY_CONFIG WHERE YY_ID = ‘"); sb.append(order.getYyId()); sb.append("‘"); //获取到微信配置参数 WxPayConfig appPayConfig = dao.getObjectBySql(sb.toString(), WxPayConfig.class); Map<String, Object> map = new HashMap<String, Object>(); //初始化一个请求对象 WxRequestHandler wxRequestHandler = new WxRequestHandler(request, response); String payFee = order.getPayMoney(); int intPayFee = (int) (Float.valueOf(payFee) * 100); String nonceStr = WxUtils.getNonceStr(); String outTradeNo = order.getOutTradeNo(); String timestamp = WxUtils.getTimeStamp(); wxRequestHandler.setParameter("appid", appPayConfig.getAppId());//appId wxRequestHandler.setParameter("mch_id", appPayConfig.getMchId());//商户号 wxRequestHandler.setParameter("nonce_str", nonceStr);//随机字符串 wxRequestHandler.setParameter("body", appPayConfig.getBody());//商品描述 wxRequestHandler.setParameter("notify_url", appPayConfig.getNotifyUrl());//通知地址 wxRequestHandler.setParameter("out_trade_no", outTradeNo); wxRequestHandler.setParameter("spbill_create_ip", request.getRemoteAddr()); wxRequestHandler.setParameter("total_fee", String.valueOf(intPayFee)); wxRequestHandler.setParameter("trade_type", "APP"); //注意签名生成方式,具体见官方文档 wxRequestHandler.setParameter("sign", wxRequestHandler.createMD5Sign(appPayConfig.getAppKey())); String prepayid; try { prepayid = wxRequestHandler.sendPrepay(); if (prepayid != null && !prepayid.equals("")) { String signs ="appid=" + appPayConfig.getAppId() + "&noncestr=" + nonceStr + "&package=Sign=WXPay" + "&partnerid="+ appPayConfig.getPartnerId() //商户id + "&prepayid=" + prepayid + "×tamp=" + timestamp + "&key=" + appPayConfig.getAppKey();//商户平台---api安全---密钥 map.put("code", 200); map.put("info", "success"); map.put("prepayid", prepayid); map.put("sign", WxUtils.getMD5Encode(signs, "utf8").toUpperCase()); map.put("appid", appPayConfig.getAppId()); map.put("timestamp", timestamp); // 等于请求prepayId时的time_start map.put("noncestr", nonceStr); // 与请求prepayId时值一致 map.put("package", "Sign=WXPay"); // 固定常量 map.put("partnerid", appPayConfig.getPartnerId());//商户id } else { map.put("code", 400); map.put("info", "获取prepayid失败"); } } catch (Exception e) { map.put("code", 405); map.put("info", "系统异常"); } return JSONUtils.toJSON(map); } @Override public String aliPay(Orders order) { //获取配置参数 BaseDao dao = DaoUtils.getDao("sys"); StringBuilder sb = new StringBuilder(); sb.append("SELECT * FROM ALI_PAY_CONFIG WHERE YY_ID = ‘"); sb.append(order.getYyId()); sb.append("‘"); AliPayConfig appPayConfig = dao.getObjectBySql(sb.toString(), AliPayConfig.class); // 实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型),为了取得预付订单信息 AlipayClient alipayClient = new DefaultAlipayClient(appPayConfig.getUrl(), appPayConfig.getAppId(), appPayConfig.getRsaPrivateKey(), appPayConfig.getFormat(), appPayConfig.getCharset(), appPayConfig.getPublicKey(), appPayConfig.getSigntype()); // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); // SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); //销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY BeanUtils.copy(order, model);//将订单信息复制到model类中 request.setBizModel(model); // 回调地址 指向回调函数 example: https://ip:port/payController/getAliPayNotify.do request.setNotifyUrl(appPayConfig.getNotifyUrl()); String orderStr = ""; try { // 这里和普通的接口调用不同,使用的是sdkExecute AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); orderStr = response.getBody(); logger.info("订单str:" + orderStr); } catch (AlipayApiException e) { logger.info(e.getMessage()); } return orderStr; } }
3.2回调代码
@Service("callBackService") public class CallBackServiceImpl implements CallBackService { private static final Log LOGGER = LogFactory.getLog(CallBackServiceImpl.class); public static final String SUCCESS = "SUCCESS"; @SuppressWarnings("unchecked") @Override public void wxCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter writer = response.getWriter(); InputStream inputstream = request.getInputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int len = 0; while((len = inputstream.read(b))!= -1){ outputStream.write(b, 0, len); } String result = new String(outputStream.toByteArray(), "utf-8"); inputstream.close(); outputStream.close(); Map<String, String> map = null; //解析微信通知返回的信息 try { map = WxUtils.doXMLParse(result); } catch (JDOMException e) { LOGGER.info("微信回调失败:"+e.getMessage()); } // 若支付成功,则告知微信服务器收到通知 if (SUCCESS.equals(map.get("return_code")) && SUCCESS.equals(map.get("result_code"))) { /** * ... * 回调业务 */ //微信会一直调用接口,直到我们返回SUCCESS String notifyStr = WxUtils.setXML(SUCCESS, ""); writer.write(notifyStr); writer.flush(); } } @Override public void aliCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException { LOGGER.info("进入支付宝回调"); // 获取支付宝GET过来反馈信息 String reqWay = ""; if ("GET".equals(request.getMethod())) { reqWay = "GET"; } Map<String, String> params = new HashMap<String, String>(); Map<?, ?> requestParams = request.getParameterMap(); for (Iterator<?> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化 if ("GET".equals(reqWay)) { try { valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { LOGGER.info("不支持的编码:" + e.getMessage()); } } params.put(name, valueStr); } String tradeNo = request.getParameter("trade_no"); // 支付宝交易号 String tradeStatus = request.getParameter("trade_status"); // 支付状态 String outTradeNo = request.getParameter("out_trade_no"); // 系统订单号 String sellerId = request.getParameter("seller_id"); // 商户号 LOGGER.info("支付宝交易号:" + tradeNo + ", 返回状态:" + tradeStatus + ",订单号 :" + outTradeNo); Map<String, Object> map = new HashMap<String, Object>(); String result = ""; try { if ("TRADE_SUCCESS".equals(tradeStatus)) { /** * ... * 回调业务 */ } else { result = "fail";// 为了保证不重复回调 } } catch (Exception e) { result = "fail"; } map.put(result, result); AliUtils.renderText(response, result); } }
3.4 微信退费的时候需要证书,密码默认是商户号,由于项目中使用了多个微信商户号,因此有多个证书,路径和密码放在了数据库中
public class WxRequestHandler{ /** * 退费 * @return */ public Map<String,String> sendWxChanel(String filePath,String pwd) throws Exception{ Map<String,String> map =null; Set es=this.getAllParameters().entrySet(); Iterator it=es.iterator(); StringBuilder sb = new StringBuilder("<xml>"); while(it.hasNext()){ Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); sb.append("<"+k+">"+v+"</"+k+">"); } sb.append("</xml>"); String params=sb.substring(0); String requestUrl = this.getGateUrl(); TenpayHttpClient httpClient = new TenpayHttpClient(); httpClient.setReqContent(requestUrl); String resContent = ""; //filepath 文件路径 pwd 密码 if (httpClient.callHttpPost(requestUrl, params,true,filePath,pwd)) { resContent = httpClient.getResContent(); map=WxUtils.doXMLParse(resContent); } return map; } }