針對APP的後臺支付程式碼(微信和支付寶)
阿新 • • 發佈:2018-11-13
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; } }