基於支付寶微信通知的一種我的收款回調方案(轉)

最近閒來無事,看到網上有一些免籤支付回調的服務商,當時感受很新奇,因而本身動手看看怎麼玩的,先當作果html

App上監聽通知並向服務器POST支付信息android

服務端的支付訂單表git

下面說原理及流程github

1.App上使用NotificationListenerService監聽通知欄通知,一旦微信支付或者支付寶收款收到消息,讀取消息的內容,而後使用正則匹配金額spring

2.App讀取到金額後,構造支付訂單,支付訂單包含:訂單號(App本身生成,不是真實的支付方訂單號),金額,App端標識,支付方式,簽名(保證數據不被篡改)數據庫

3.App將訂單POST到填寫的URL中json

4.服務端收到訂單信息,先校驗簽名是否相符,再查看訂單是否存在(防止重放攻擊),驗證經過後存入數據庫,並向指定的回調地址發起請求springboot

5.服務端若是向指定的回調地址發起請求失敗,使用定時任務重複發起回調,直到回調成功或達到指定次數服務器

 

以上就是所有過程,服務端使用springboot,能夠很快速搭建微信

固然爲了保證可靠性須要給App加固,防止退出,還有這種只能讀取到金額,其餘信息一無所知,有些侷限性

2019-03-14補充:

代碼很簡單,上傳github徹底是小題大作,下面貼出關鍵代碼

App部分

繼承NotificationListenerService重寫onNotificationPosted方法

 

複製代碼
  1 //來通知時的調用
  2     @Override
  3     public void onNotificationPosted(StatusBarNotification sbn) {
  4         Notification notification = sbn.getNotification();
  5         if (notification == null) {
  6             return;
  7         }
  8         Bundle extras = notification.extras;
  9         if (extras != null) {
 10             //包名
 11             String pkg = sbn.getPackageName();
 12             // 獲取通知標題
 13             String title = extras.getString(Notification.EXTRA_TITLE, "");
 14             // 獲取通知內容
 15             String content = extras.getString(Notification.EXTRA_TEXT, "");
 16             Log.i(TAG, String.format("收到通知,包名:%s,標題:%s,內容:%s", pkg, title, content));
 17             //處理
 18             processOnReceive(pkg, title, content);
 19         }
 20     }
 21 
 22 /**
 23      * 消息來時處理
 24      *
 25      * @param pkg
 26      * @param title
 27      * @param content
 28      */
 29     private void processOnReceive(String pkg, String title, String content) {
 30         if (!AppConstants.LISTEN_RUNNING) {
 31             return;
 32         }
 33         if ("com.eg.android.AlipayGphone".equals(pkg)) {
 34             //支付寶
 35             if (checkMsgValid(title,content,"alipay") && StringUtils.isNotBlank(parseMoney(content))) {
 36                 TreeMap<String, String> paramMap = new TreeMap<>();
 37                 paramMap.put("title", title);
 38                 paramMap.put("content", content);
 39                 paramMap.put("identifier", AppConstants.CLIENT_IDENTIFIER);
 40                 paramMap.put("orderid", CommonUtils.randomCharSeq());
 41                 paramMap.put("gateway", "alipay");
 42                 String sign = CommonUtils.calcSign(paramMap, AppConstants.SIGN_KEY);
 43                 if (StringUtils.isBlank(sign)) {
 44                     Log.e(TAG, "簽名錯誤");
 45                     return;
 46                 }
 47                 HttpTask task = new HttpTask();
 48                 task.setOnAsyncResponse(this);
 49                 String json = new Gson().toJson(paramMap);
 50                 task.execute(AppConstants.POST_URL, "sign=" + sign, json);
 51             }
 52         } else if ("com.tencent.mm".equals(pkg)) {
 53             //微信
 54             if (checkMsgValid(title, content, "wxpay") && StringUtils.isNotBlank(parseMoney(content))) {
 55                 TreeMap<String, String> paramMap = new TreeMap<>();
 56                 paramMap.put("title", title);
 57                 paramMap.put("content", content);
 58                 paramMap.put("identifier", AppConstants.CLIENT_IDENTIFIER);
 59                 paramMap.put("orderid", CommonUtils.randomCharSeq());
 60                 paramMap.put("gateway", "wxpay");
 61                 String sign = CommonUtils.calcSign(paramMap, AppConstants.SIGN_KEY);
 62                 if (StringUtils.isBlank(sign)) {
 63                     Log.e(TAG, "簽名錯誤");
 64                     return;
 65                 }
 66                 HttpTask task = new HttpTask();
 67                 task.setOnAsyncResponse(this);
 68                 String json = new Gson().toJson(paramMap);
 69                 task.execute(AppConstants.POST_URL, "sign=" + sign, json);
 70             }
 71         }
 72     }
 73 
 74 /**
 75      * 解析內容字符串,提取金額
 76      *
 77      * @param content
 78      * @return
 79      */
 80     private static String parseMoney(String content) {
 81         Pattern pattern = Pattern.compile("收款(([1-9]\\d*)|0)(\\.(\\d){0,2})?元");
 82         Matcher matcher = pattern.matcher(content);
 83         if (matcher.find()) {
 84             String tmp = matcher.group();
 85             Pattern patternnum = Pattern.compile("(([1-9]\\d*)|0)(\\.(\\d){0,2})?");
 86             Matcher matchernum = patternnum.matcher(tmp);
 87             if (matchernum.find())
 88                 return matchernum.group();
 89         }
 90         return null;
 91     }
 92 
 93     /**
 94      * 驗證消息的合法性,防止非官方消息被處理
 95      *
 96      * @param title
 97      * @param content
 98      * @param gateway
 99      * @return
100      */
101     private static boolean checkMsgValid(String title, String content, String gateway) {
102         if ("wxpay".equals(gateway)) {
103             //微信支付的消息格式
104             //1條:標題:微信支付,內容:微信支付收款0.01元(朋友到店)
105             //多條:標題:微信支付,內容:[4條]微信支付: 微信支付收款1.01元(朋友到店)
106             Pattern pattern = Pattern.compile("^((\\[\\+?\\d+條])?微信支付:|微信支付收款)");
107             Matcher matcher = pattern.matcher(content);
108             return "微信支付".equals(title) && matcher.find();
109         } else if ("alipay".equals(gateway)) {
110             //支付寶的消息格式,標題:支付寶通知,內容:支付寶成功收款1.00元。
111             return "支付寶通知".equals(title);
112         }
113         return false;
114     }
複製代碼

 

服務端接收代碼

複製代碼
/**
     * 接受App發送的通知內容
     * @param content   通知內容json, {"title": "標題", "content": "內容", "identifier": "app端標識", "orderid": "app生成的惟一訂單號", "gateway": "wxpay或alipay"}
     * @param sign      簽名,簽名方式按照content對應的key1=vaule1&key2=value2...&SECKEY計算md5,key的順序按字母表的順序
     * @return
     */
    @RequestMapping(value = "/c/post/notification", method = { RequestMethod.POST })
    @ResponseBody
    public String receiveAppNotification(@RequestBody Map<String, Object> content, String sign) {
        logger.debug("請求參數,content=>{}, sign=>{}", JSON.toJSONString(content), sign);
        if (StringUtils.isBlank(sign) || CollectionUtils.isEmpty(content)) {
            return APIUtil.getReturn(APIConst.PARAM_ERROR);
        }
        //再次驗證字段
        String contenttext = (String) content.get("content");
        String identifier = (String) content.get("identifier");
        String orderid = (String) content.get("orderid");
        String gateway = (String) content.get("gateway");
        if (StringUtils.isAnyBlank(contenttext, identifier, orderid, gateway) || !ImmutableList.of("alipay",
                "wxpay").contains(gateway)) {
            return APIUtil.getReturn(APIConst.PARAM_ERROR);
        }
        //讀取金額(單位元)
        Pattern pattern = Pattern.compile("([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*)");
        Matcher matcher = pattern.matcher(contenttext);
        if (!matcher.find()) {
            return APIUtil.getReturn(APIConst.PARAM_ERROR);
        }
        String amountStr = matcher.group(1);
        logger.debug("解析的金額:{}", amountStr);
        BigDecimal amount = null;
        try {
            amount = new BigDecimal(amountStr);
        } catch (NumberFormatException e) {
            logger.error("金額格式錯誤: {}", amountStr);
            return APIUtil.getReturn(APIConst.PARAM_ERROR);
        }

        //驗證簽名
        TreeMap<String, Object> paramMap = new TreeMap<>(content);
        Iterator<Map.Entry<String, Object>> it = paramMap.entrySet().iterator();
        StringBuilder sb = new StringBuilder();
        while (it.hasNext()) {
            Map.Entry<String, Object> entry = it.next();
            sb.append(entry.getKey());
            sb.append("=");
            sb.append(entry.getValue());
            sb.append("&");
        }
        sb.append(SIGN_KEY);
        //計算簽名
        String calcSign = MD5Util.MD5Encode(sb.toString(), "UTF-8");
        if (!calcSign.equalsIgnoreCase(sign)) {
            return APIUtil.getReturn(1, "簽名錯誤");
        }

        //查詢訂單號是否已經存在
        boolean exist = orderService.checkOrderExist(orderid);
        if (exist) {
            logger.error("訂單號:{}已存在", orderid);
            return APIUtil.getReturn(1, "訂單號已存在");
        }

        //訂單寫入數據庫
        String account = "";
        if (gateway.equals("wxpay")) {
            account = "W" + identifier;
        } else if (gateway.equals("alipay")) {
            account = "A" + identifier;
        }
        MqOrder order = new MqOrder();
        order.setAccount(account);
        order.setAmount(amount);
        order.setGateway(gateway);
        order.setOrderId(orderid);
        order.setStatus(0);
        order.setNotifyCount(0);
        order.setCreateTime(new Date());
        orderService.save(order);

        return APIUtil.getReturn(APIConst.OK);
    }
複製代碼

 歡迎學習交流,qq:3168598325

轉自http://www.javashuo.com/article/p-wrtinzux-v.html

相關文章
相關標籤/搜索