因公司业务需求,支付选用第三方beecloud作为支付平台,一下是移动端APP支付流程和后台java代码,其他方式类同;
下图为整个支付的流程:
其中需要开发者开发的只有:
步骤①(在App端)发送订单信息
做完这一步之后就会跳到相应的支付页面(如微信app中),让用户继续后续的支付步骤
步骤②:(在App端)处理同步回调结果
付款完成或取消之后,会回到客户app中,在页面中展示支付结果(比如弹出框告诉用户“支付成功"或"支付失败”)。同步回调结果只作为界面展示的依据,不能作为订单的最终支付结果,最终支付结果应以异步回调为准。
步骤③:(在客户服务端)处理异步回调结果(Webhook)
付款完成之后,根据客户在BeeCloud后台的设置,BeeCloud会向客户服务端发送一个Webhook请求,里面包括了数字签名,订单号,订单金额等一系列信息。客户需要在服务端依据规则要验证数字签名是否正确,购买的产品与订单金额是否匹配,这两个验证缺一不可。验证结束后即可开始走支付完成后的逻辑。
Webhook是BeeCloud获得渠道的确认信息后,立刻向商户服务器发送的异步回调。支付,代付,退款成功时,BeeCloud将向商户指定的URL发送HTTP/HTTPS的POST数据请求。
如果商户需要接收此类消息来实现业务逻辑,需要:
- 开通公网可以访问的IP地址(或域名)和端口(如果需要对传输加密,请使用支持HTTPS的URL地址,BeeCloud不要求HTTPS根证书认证)
- 在 控制台->选择应用->应用设置->Webhook参数设置 中设置接收端URL,不同应用可设置不同URL,同一应用能且仅能设置一个测试URL,一个生产URL,另外在控制台->应用设置->基本信息设置中获取"Master Secret"
- 处理POST请求报文,实现业务逻辑
一下是后端java代码实现
@RequestMapping(value = "/webhookReceiver") String webhookReceiver(HttpServletRequest request) { ResultMessage rm = new ResultMessage(ResultConstant.FAIL); String appID = " .................."; String testSecret = "......................"; String appSecret = "................................"; String masterSecret = ".........................."; BeeCloud.registerApp(appID, testSecret, appSecret, masterSecret); StringBuffer json = new StringBuffer(); String line = null; try { request.setCharacterEncoding("utf-8"); BufferedReader reader = request.getReader(); while ((line = reader.readLine()) != null) { json.append(line); } } catch (Exception e) { e.printStackTrace(); } JSONObject jsonObj = JSONObject.fromObject(json.toString()); String signature = jsonObj.getString("signature"); String transactionId = jsonObj.getString("transaction_id"); String transactionType = jsonObj.getString("transaction_type"); String channelType = jsonObj.getString("channel_type"); String transactionFee = jsonObj.getString("transaction_fee"); StringBuffer toSign = new StringBuffer(); toSign.append(BCCache.getAppID()).append(transactionId) .append(transactionType).append(channelType) .append(transactionFee); boolean status = verifySign(toSign.toString(), masterSecret, signature); //请不要修改或删除 if (status) { //验证成功 boolean b = verifyMoney(transactionFee, channelType, transactionId); if (!b) { logger.error("=============================》付款的钱和数据库里面存的钱不一致《============================================="); } //必须返回success return "success"; } else { //验证失败 return "fail"; } } //校验支付的这笔钱是否和数据库里面存的钱相等- boolean verifyMoney(String transactionFee, String channelType, String transactionId) { RpcResult thirdpartyItemInfo = showOrderProvider.getThirdpartyItemInfo(transactionId); Map map = (Map) thirdpartyItemInfo.getValue(); String service_fee = map.get("service_fee").toString(); if (transactionFee.equals(service_fee)) { SsisPublishServiceOrderItem orderItem = new SsisPublishServiceOrderItem(); orderItem.setItemId(transactionId); orderItem.setPayTime(new Date()); orderItem.setPayType(channelType); orderItem.setPayStatus("paid"); RpcResult rpc = publishOrderProvider.updateThirdpartyItem(orderItem); if (!rpc.hasException()) { return true; } } return false; } //下面的方法不要修改 boolean verifySign(String text, String masterKey, String signature) { boolean isVerified = verify(text, signature, masterKey, "UTF-8"); if (!isVerified) { return false; } return true; } boolean verify(String text, String sign, String key, String inputCharset) { text = text + key; String mysign = DigestUtils.md5Hex(getContentBytes(text, inputCharset)); return mysign.equals(sign); } byte[] getContentBytes(String content, String charset) { if (charset == null || "".equals(charset)) { return content.getBytes(); } try { return content.getBytes(charset); } catch (UnsupportedEncodingException e) { throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset); } }