package jframe.pay.wx.service; import java.net.URL; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jframe.core.plugin.annotation.InjectPlugin; import jframe.core.plugin.annotation.InjectService; import jframe.core.plugin.annotation.Injector; import jframe.core.plugin.annotation.Start; import jframe.core.plugin.annotation.Stop; import jframe.httpclient.service.HttpClientService; import jframe.memcached.client.MemcachedService; import jframe.pay.dao.service.PayDaoService; import jframe.pay.domain.PayCurrency; import jframe.pay.domain.PayStatus; import jframe.pay.domain.TradeType; import jframe.pay.domain.dao.OrderWx; import jframe.pay.domain.http.RspCode; import jframe.pay.domain.util.HttpUtil; import jframe.pay.domain.util.ObjectUtil; import jframe.pay.domain.util.XmlUtil; import jframe.pay.wx.WxpayPlugin; import jframe.pay.wx.domain.WxConfig; import jframe.pay.wx.domain.WxFields; import jframe.pay.wx.http.client.WxServiceNew; @Injector public class WxpayServiceImpl implements WxpayService, WxFields { static Logger LOG = LoggerFactory.getLogger(WxpayServiceImpl.class); @InjectService(id = "jframe.pay.service.dao") static PayDaoService PayDao; @InjectPlugin static WxpayPlugin Plugin; @InjectService(id = "jframe.service.httpclient") static HttpClientService HttpClient; @InjectService(id = "jframe.service.memcached.client") static MemcachedService MemSvc; static Map<String, String> HTTP_PARAS = new HashMap<String, String>(1, 1); static { HTTP_PARAS.put(HttpClientService.P_MIMETYPE, "application/x-www-form-urlencoded"); // HTTP_PARAS.put(HttpClientService.P_METHOD, "post"); } @Start void start() { try { WxConfig.GroupID = Plugin.getConfig("groupid.wxpay", WxConfig.GroupID); WxConfig.init(Plugin.getConfig(WxConfig.CONF_FILE_NAME)); } catch (Exception e) { LOG.error("Load wxconfig error {}", e.getMessage()); } } @Stop void stop() { } @Override public void pay(Map<String, String> req, Map<String, Object> rsp) throws Exception { // check req if (HttpUtil.mustReq(req, F_payNo, F_payAmount, F_payDesc, F_remoteIp).size() > 0) { RspCode.setRspCode(rsp, RspCode.FAIL_HTTP_MISS_PARA); return; } if (PayDao == null) { RspCode.setRspCode(rsp, RspCode.FAIL_DB_Conn); return; } if (TradeType.JSAPI.code.equals(req.get(F_tradeType))) { if (HttpUtil.mustReq(req, F_openid).size() > 0) { RspCode.setRspCode(rsp, RspCode.FAIL_HTTP_MISS_PARA); return; } } // String token = WxUtil.getToken(); // // TODO 判断有效性的方式 pakcageValue如何设置 // if (ObjectUtil.isEmpty(token)) { // RspCode.setRspCode(rsp, RspCode.FAIL_TOKEN); // return; // } // req.put(F_token, String.valueOf(token)); if (WxServiceNew.genPrePay(req, rsp)) { OrderWx od = PayDao.selectOrderWx(req.get(F_payNo)); boolean insert = false; if (od == null) { insert = true; od = new OrderWx(); od.payNo = req.get(F_payNo); od.payStatus = PayStatus.C_PAY_WAIT.code; } else { // TODO 已成功单子处理 } od.payDesc = req.get(F_payDesc); od.transType = req.get(F_transType); od.payAmount = Long.parseLong(req.get(F_payAmount)); od.payGroup = req.get(F_payGroup); od.payCurrency = req.getOrDefault(F_payCurrency, PayCurrency.CNY.code); od.payTimeout = Long.parseLong(req.getOrDefault(F_payTimeout, "-1")); if (od.payTimeout == -1) { // TODO config od.payTimeout = new Date().getTime() + 72 * 3600 * 1000; } od.backUrl = req.get(F_backUrl); od.account = req.get(F_account); od.orderNo = req.get(F_orderNo); if (insert) { PayDao.insertOrderWx(od); } else PayDao.updateOrderWx(od); // TODO insert order detail // TODO insert task RspCode.setRspCode(rsp, RspCode.SUCCESS); return; } rsp.clear(); RspCode.setRspCode(rsp, RspCode.FAIL_NET); } static final ConcurrentMap<String, String> PayBackLock = new ConcurrentHashMap<>(); /** * <pre> * 通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒 * </pre> */ @Override public void payBack(Map<String, String> req, Map<String, Object> rsp) throws Exception { if (!chkPayBack(req)) { return; } try { // for cluster TODO concurrent MemSvc.add("wx.payback." + req.get(WxFields.F_out_trade_no), "1", new Date(15 * 1000)); String orderStatus = PayStatus.C_PAY_FAIL.code; if (WxServiceNew.backPay(req, rsp)) { orderStatus = PayStatus.C_PAY_SUC.code; // SUCCESS/FAIL Map<String, String> body = new HashMap<>(2, 1); body.put("return_code", "<![CDATA[SUCCESS]]>"); body.put("return_msg", "<![CDATA[OK]]>"); rsp.put(F_rspOut, XmlUtil.toXml("xml", body)); } req.put(F_payStatus, orderStatus); wxPayBack(req, rsp); } finally { PayBackLock.remove(req.get(WxFields.F_out_trade_no)); MemSvc.delete("wx.payback." + req.get(WxFields.F_out_trade_no)); } } /** * TODO BitSet * * @param req * @return */ private boolean chkPayBack(Map<String, String> req) { String od = req.get(WxFields.F_out_trade_no); String ood = PayBackLock.putIfAbsent(od, ""); boolean chk = ood == null ? true : false; if (chk) { chk = MemSvc.get("wx.payback." + od) == null ? true : false; } // clean if (ood == null && !chk) PayBackLock.remove(od); return chk; } @Override public void goodReturn(Map<String, String> req, Map<String, Object> rsp) { // String[] reqParams = { F_accountFrom, F_accountTo, F_usrTo, // F_payGroup, // F_payNo, F_transType, F_payAmount }; // if (!RequestUtil.isValidRequest(req, respMap, reqParams)) { // return; // } // // if (_dsSvc == null) { // respMap.put(F_respCode, RespCode.FAIL_DB_Conn.code); // respMap.put(F_respMsg, RespCode.FAIL_DB_Conn.desc); // return; // } // // Connection conn = null; // boolean autoCommit = true; // // try { // conn = _dsSvc.getConnection(); // autoCommit = conn.getAutoCommit(); // conn.setAutoCommit(false); // // OrderService orderService = new OrderService(); // OrderWX pay = orderService // .getOrderWXByPayNo(conn, req.get(F_payNo)); // // if (pay == null) { // RspCode.setRspCode(rsp, RspCode.FAIL_PAYNO_NOEXIST); // return; // } // if (PayStatus.R_SUC.code.equals(pay.getOrderStatus())) { // RspCode.setRspCode(rsp, RspCode.SUCCESS); // return; // } // // Map<String, String> resp = new HashMap<String, String>(); // if (WxService.refund(pay, resp)) { // int refundStatus = Integer.valueOf(resp.get("refund_status")); // if (refundStatus == 4 || refundStatus == 10) { // req.put(F_orderNo, pay.getOrderNum()); // String prevFlowId = queryOrderFlowId(req); // String flowId = IDUtil.genFlowId(); // // insert flowid // this.insertOrderFlow(conn, req, flowId, prevFlowId); // // update order // this.updateOrder_Cancel(conn, req, resp, flowId, pay); // // conn.commit(); // RspCode.setRspCode(rsp, RspCode.SUCCESS); // return; // } else { // LOG.error("orderNum={},refund_status={}", // resp.get(F_orderNo), resp.get("refund_status")); // } // } else { // LOG.error("rspCode={},rspDesc={}", resp.get(F_rspCode), // resp.get(F_rspDesc)); // } // } catch (Exception e) { // LOG.error(e.getMessage()); // if (conn != null) { // try { // conn.rollback(); // } catch (Exception e2) { // LOG.error(e2.getMessage()); // } // } // } finally { // if (conn != null) { // try { // conn.setAutoCommit(autoCommit); // _dsSvc.recycleConnection(conn); // } catch (Exception e) { // LOG.error(e.getMessage()); // } // } // } RspCode.setRspCode(rsp, RspCode.FAIL_NET); } public void wxPayBack(Map<String, String> req, Map<String, Object> rsp) throws Exception { String orderNo = req.get(WxFields.F_out_trade_no); OrderWx od = PayDao.selectOrderWxWithOrderNo(orderNo); if (od == null) throw new Exception("OrderWx is not found, orderNo ->" + orderNo); if (PayStatus.C_PAY_SUC.code.equals(od.payStatus)) { RspCode.setRspCode(rsp, RspCode.SUCCESS); return; } // od.tradeMode = Integer.parseInt(req.get(WxFields.F_trade_mode)); od.payStatus = req.get(WxFields.F_payStatus); // od.payInfo = req.get(WxFields.F_pay_info); od.bankType = req.get(WxFields.F_bank_type); // od.bankBillNo = req.get(WxFields.F_bank_billno); // od.notifyId = req.get(WxFields.F_notify_id); od.transactionId = req.get(WxFields.F_transaction_id); od.timeEnd = req.get(F_time_end); // od.transportFee = // Integer.parseInt(req.getOrDefault(WxFields.F_transport_fee, "0")); // transport_fee + product_fee=total_fee // od.productFee = // Integer.parseInt(req.getOrDefault(WxFields.F_product_fee, "0")); // 折扣价格,单位分,如果 有值,通知的 total_fee + discount = 请求的 total_fee // od.discount = Integer.parseInt(req.get(WxFields.F_discount)); // od.buyerAlias = req.get(WxFields.F_buyerAlias); od.orderFinishTime = new Date().getTime(); od.openid = req.get(WxFields.F_openid); PayDao.updateOrderWx(od); // TODO postBack until return success String orderStatus = od.payStatus; if (ObjectUtil.notEmpty(od.backUrl) && (PayStatus.C_PAY_SUC.code.equals(orderStatus) || PayStatus.C_PAY_FAIL.code.equals(orderStatus) || PayStatus.C_PAY_TIMEOUT.code.equals(orderStatus))) { postBack(od); } else { LOG.error("wx callback orderStatus=" + orderStatus); } } /** * 通知租车后台 * * @param conn * @param od * @param orderStatus */ public void postBack(OrderWx od) { Map<String, String> map = new HashMap<String, String>(2, 1); map.put(F_payNo, od.payNo); map.put(F_payStatus, od.payStatus); boolean succ = false; try { URL url = new URL(od.backUrl); int port = url.getPort() == -1 ? 80 : url.getPort(); if (LOG.isDebugEnabled()) LOG.debug("postBack -> {}, {},{}", url.toString(), new Date(), map); Long packtime = System.currentTimeMillis(); Map<String, String> paras = new HashMap<>(HTTP_PARAS); paras.put("ip", url.getHost()); paras.put("port", String.valueOf(port)); Map<String, String> rsp = HttpClient.<HashMap<String, String>> send("payback", url.getPath(), HttpUtil.format(map, "utf-8"), null, paras); Long packTime = System.currentTimeMillis(); if (LOG.isDebugEnabled()) LOG.debug("orderNo=" + od.orderNo + " postBack" + new Date() + " use time=" + (packTime - packtime) + " rsp=" + rsp); if (RspCode.SUCCESS.code.equals(rsp.get(F_rspCode))) { succ = true; } else { LOG.error("payNo=" + od.payNo + "rsp=" + rsp); } } catch (Exception e) { succ = false; LOG.error(e.getMessage()); } if (!succ) { // TODO // map.put(F_backUrl, order.getBackUrl()); // Task t = createTask(TaskType.PAY_BACK.type, // TaskType.PAY_BACK.desc, // JsonUtil.encode(map), new Date().getTime()); // try { // insertTask_Order(conn, t); // } catch (Exception e1) { // LOG.error(e1.getMessage()); // } } } }