“一码多付”,微信支付、支付宝支付

     写着写着,越写越多,思绪写不下了,回头写在前面的话。此系统属个人创业项目,历时3个月,主要实现自助按摩椅、娃娃机、自动换币器功能,主要流程就是用户扫码二维码->授权获取用户信息->弹出H5界面选择相应服务->支付->控制硬件设备做对应操作。主要功能点如下:

1)微信、支付宝用户信息获取、保存会员信息。具体步骤见各自api文档,十分详细,同时坑也很多。

2)一码多付,微信公众号支付,支付宝支付,需按照步骤流程一步步走,一步步申请,设置,同时坑更多,有问题可随时沟通交流。

3)订单查询、收入明细、统计,充值管理等。

 

4)后台系统的账户、权限、统计信息、数据导出等,用shiro 控制

 

5)设备的添加和维护,二维码的生成、代金券的生成、用户批量绑定代金券,合作商管理,加盟商管理等。

 

6)短信网关、邮件功能。

 

7)微信公众号对接,自动回复,客服消息,各种事件推送。

 

马上年终总结了,坐等年终大奖,也没多少时间写了,想到哪里就写点吧,有疑问的话可加qq:540769049 进行交流,根据大家疑问慢慢完善代码吧,请备注来由。

回头把二维码放出来,关注公众号后回复测试账号,就能体验系统各模块功能了,比这里啰里啰嗦简单粗暴。

一:业务需求

通过一个二维码完成多种第三方支付方式支付的需求:

商户打印一个静态的二维码,顾客用微信、支付宝扫这个二维码后,进入商户的一个付款页面,输入金额(或选择特定金额)后,完成支付,然后启动后续服务流程。

本demo实现一码多付(微信公众号、支付宝)支付后,控制按摩椅的业务需求。

二:业务实现

1.生成二维码

生成带有设备参数(唯一性)的二维码,用于用户扫码时定位具体的设备,用户支付成功后,开启后续服务。

一般建议生成二维码的url不要太长,长度越短则生成的二维码识别率越高。二维码url示例:http//xyxspace.com/m/123456

2.扫码识别

       用户扫码时,可能会有许多种软件来扫,但目前只实现了微信、支付宝扫码支付。因此需要做识别扫码来源并在页面作出提示,引导用户用支微信、支付宝进行扫码。

      用户用app扫商户的二维码后,其实是用app浏览器打开到商户的页面, 商户页面通过识别浏览器header中的user-agent来判断是哪个app打开的。 

 

常见App浏览器的user-agent 识别关键字:

支付宝: AlipayClient

微信: MicroMessenger

如果识别到不包含上述关键信息,则跳转到错误提示页面,引导用户用微信、支付宝扫码。如下图:

 

 

 

 

微信、支付宝扫码成功后,会将设备mcode提交到你的服务端,保存在session中或放在下一步授权回调地址参数中。

 

3.技术实现

本demo使用springmvc+mybatis3.2后台框架,mysql5.7数据库,HTML5+css3.0+bootstrap前端页面,shiro 、ehcache 、druid等技术。

 

4.话不多说,开始撸代码......

 

    1.扫码入口控制类:采用springmvc架构,用户扫码后到控制类;

          1)识别二维码中的mcode,保存到session中。

          2)判断用户是否登录,如果没有登录,根据APP类型,获取用户信息,作为会员信息保存到数据库。

          3)如果已登录,跳转到价格页面。

/**
 * 扫码入口控制类
 * 
 */
@Controller
@RequestMapping(value = "/m")
public class IndexController extends BaseController {

	@Resource(name = "commonService")
	private CommonService commonService;

	@Resource(name = "indexService")
	private IndexService indexService;

	/**
	 * 扫码总入口
	 * 
	 * @return ModelAndView
	 */
	@RequestMapping(value = "/3*")
	public ModelAndView scaning() {
		String url = request.getRequestURL().toString();
		String mcode = url.substring(url.lastIndexOf("/") + 1);
		session.setAttribute("mcode", mcode);
		String id = "";
		if (Constants.WEIXIN.equals(appType)) {// 来自微信
			logger.info("微信扫码...");
			id = (String) session.getAttribute("openid");
		} else if (Constants.ALIPAY.equals(appType)) {// 来自支付宝
			logger.info("支付宝扫码...");
			id = (String) session.getAttribute("user_id");
		}
		if (StringUtils.isBlank(id)) {// session中未存在用户信息时,授权重新获取用户信息
			return indexService.createRedirectURL(appType);
		} else {
			return commonService.trunView(id, mcode, basePath);// 跳转到首页价格页面
		}
	}

}

2.判断扫码APP类型

 

public static String IsWeixinOrAlipay(HttpServletRequest request) {
		if (request != null) {
			String userAgent = request.getHeader("user-agent");
			if (StringUtils.isNotBlank(userAgent)) {
				userAgent = userAgent.toLowerCase();
				if (userAgent.indexOf("micromessenger") > -1) {// 微信客户端
					return Constants.WEIXIN;
				} else if (userAgent.indexOf("alipayclient") > -1) {
					return Constants.ALIPAY;
				}
			}
		}
		return "other";
	}

3.获取微信用户信息
  1) 根本客户端类型生成对应授权url

 

 

public ModelAndView createRedirectURL(String appType) {
		if (Constants.WEIXIN.equals(appType)) {// 微信客户端
			String redirectUrl = OAuthManager.generateRedirectURI(WXConstants.REDIRECT_URI, WXConstants.SCOPE, WXConstants.STATE);
			return new ModelAndView("redirect:" + redirectUrl);
		} else if (Constants.ALIPAY.equals(appType)) {// 支付宝客户端
			String redirectUrl = OAuthALiService.generateRedirectURI(AlipayConfig.REDIRECTURI, AlipayConfig.ALIPAY_SCOPE, AlipayConfig.STATE);
			return new ModelAndView("redirect:" + redirectUrl);
		} else {
			ModelAndView mv = new ModelAndView();
			mv.setViewName("view/index/unAllow");
			return mv;
		}
	}

2)获取微信授权url

/**
 * 网页授权获取用户基本信息
 * <p>
 * 参考<a href="http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html">开发文档</a>
 * </p>
 * Created by xuwen on 2015/12/11.
 */
public class OAuthManager {

	private static Logger logger = Logger.getLogger(OAuthManager.class);

	/* 生成OAuth重定向URI(用户同意授权,获取code) */
	private static final String HTTPS_OPEN_WEIXIN_QQ_COM_CONNECT_OAUTH2_AUTHORIZE = "https://open.weixin.qq.com/connect/oauth2/authorize";
	/* 通过code换取网页授权access_token */
	private static final String HTTPS_API_WEIXIN_QQ_COM_SNS_OAUTH2_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token";
	/* 刷新access_token(如果需要) */
	private static final String HTTPS_API_WEIXIN_QQ_COM_SNS_OAUTH2_REFRESH_TOKEN = "https://api.weixin.qq.com/sns/oauth2/refresh_token";
	/* 拉取用户信息(需scope为 snsapi_userinfo) */
	private static final String HTTPS_API_WEIXIN_QQ_COM_SNS_USERINFO = "https://api.weixin.qq.com/sns/userinfo";
	/* 检验授权凭证(access_token)是否有效 */
	private static final String HTTPS_API_WEIXIN_QQ_COM_SNS_AUTH = "https://api.weixin.qq.com/sns/auth";

	/**
	 * 生成OAuth重定向URI(用户同意授权,获取code)
	 * <p>
	 * 参考<a href="http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html#.E7.AC.AC.E4.B8.80.E6.AD.A5.EF.BC.9A.E7.94.A8.E6.88.B7.E5.90.8C.E6.84.8F.E6.8E.88.E6.9D.83.EF.BC.8C.E8.8E.B7.E5.8F.96code">开发文档</a>
	 * </p>
	 * 
	 * @param redirectURI
	 * @param scope
	 * @param state
	 * @return
	 */
	public static String generateRedirectURI(String redirectURI, String scope, String state) {
		StringBuffer url = new StringBuffer();
		url.append(HTTPS_OPEN_WEIXIN_QQ_COM_CONNECT_OAUTH2_AUTHORIZE);
		url.append("?appid=").append(urlEncode(Config.instance().getAppid()));
		url.append("&redirect_uri=").append(urlEncode(redirectURI));
		url.append("&response_type=code");
		url.append("&scope=").append(urlEncode(scope));
		url.append("&state=").append(urlEncode(state));
		url.append("#wechat_redirect");
		return url.toString();
	}

	/**
	 * 通过code换取网页授权access_token
	 * <p>
	 * 参考<a href="http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html#.E7.AC.AC.E4.BA.8C.E6.AD.A5.EF.BC.9A.E9.80.9A.E8.BF.87code.E6.8D.A2.E5.8F.96.E7.BD.91.E9.A1.B5.E6.8E.88.E6.9D.83access_token">开发文档</a>
	 * </p>
	 * 
	 * @param request
	 * @return
	 */
	public static GetAccessTokenResponse getAccessToken(GetAccessTokenRequest request) throws OAuthException {
		String response = post(HTTPS_API_WEIXIN_QQ_COM_SNS_OAUTH2_ACCESS_TOKEN, request);
		check(response);
		return JSONObject.parseObject(response, GetAccessTokenResponse.class);
	}

	/**
	 * 拉取用户信息(需scope为 snsapi_userinfo)
	 * <p>
	 * 参考<a href="http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html#.E7.AC.AC.E5.9B.9B.E6.AD.A5.EF.BC.9A.E6.8B.89.E5.8F.96.E7.94.A8.E6.88.B7.E4.BF.A1.E6.81.AF.28.E9.9C.80scope.E4.B8.BA_snsapi_userinfo.29">开发文档</a>
	 * </p>
	 * 
	 * @param request
	 * @return
	 */
	public static GetUserinfoResponse getUserinfo(GetUserinfoRequest request) throws OAuthException {
		String response = post(HTTPS_API_WEIXIN_QQ_COM_SNS_USERINFO, request);
		check(response);
		return JSONObject.parseObject(response, GetUserinfoResponse.class);
	}
}

3)微信auth2.0 回调方法

 

 

@RequestMapping(value = "/oauthCallback")
	public ModelAndView oauthCallback() {
		logBefore(logger, "weixin回调方法...");
		// 获取code
		String code = request.getParameter("code");
		GetAccessTokenResponse getAccessTokenResponse;
		try {
			getAccessTokenResponse = OAuthManager.getAccessToken(new GetAccessTokenRequest(code));
			GetUserinfoResponse getUserinfoResponse = OAuthManager.getUserinfo(new GetUserinfoRequest(getAccessTokenResponse.getAccess_token(), getAccessTokenResponse.getOpenid()));
			logger.info("oauth2获取到的用户信息:[" + getUserinfoResponse + "]");
			// 保存openid到session
			String openid = getUserinfoResponse.getOpenid();
			session.setAttribute("openid", openid);
			session.setAttribute("wxuser", getUserinfoResponse);
			// 保存用户信息
			Map<String, Object> map = BeanUtil.transBean2Map(getUserinfoResponse);
			map.put("add_ip", getIp());
			map.put("add_time", System.currentTimeMillis() / 1000);
			memberService.saveOrUpdateMemberOfWX(map);
			ModelAndView mv = this.getModelAndView();
			mv.setViewName("view/index/index");
			return mv;
		} catch (OAuthException e) {
			e.printStackTrace();
		}
		return null;
	}

4)支付宝回调方法

 

 

/**
	 * 授权回调方法
	 * 
	 * @return
	 */
	@RequestMapping(value = "/oauthCallback")
	public ModelAndView oauthCallback() {
		logger.info("ali授权回调方法");
		// 2.获取授权码auth_code
		String auth_code = request.getParameter("auth_code");
		AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
		request.setCode(auth_code);// 授权码
		request.setGrantType("authorization_code");// 授权类型
		AlipaySystemOauthTokenResponse oauthTokenResponse;
		try {
			// 3.通过auth_code换取access_token及用户userId
			oauthTokenResponse = alipayClient.execute(request);
			String accessToken = oauthTokenResponse.getAccessToken();
			// 4.调用接口获取用户信息.如果scope=auth_base,在第三步就可以获取到用户的userId,无需走第四步。如果scope=auth_user,才需要走第四步,通过access_token调用用户信息共享接口获取用户信息。
			AlipayUserInfoShareRequest request1 = new AlipayUserInfoShareRequest();
			AlipayUserInfoShareResponse userinfoShareResponse = alipayClient.execute(request1, accessToken);
			logger.info(userinfoShareResponse.getBody());
			session.setAttribute("user_id", userinfoShareResponse.getUserId());
			session.setAttribute("aliuser", userinfoShareResponse);
			// 保存用户信息
			Map<String, Object> map = BeanUtil.transBean2Map(userinfoShareResponse);
			map.put("add_ip", getIp());
			map.put("add_time", System.currentTimeMillis() / 1000);
			memberService.saveOrUpdateMemberOfALipay(map);
			ModelAndView mv = this.getModelAndView();
			mv.setViewName("view/index/index");
			return mv;
		} catch (AlipayApiException e) {
			e.printStackTrace();
		}
		return null;
	}

5)获取到的用户信息效果图

 


 

6)用户信息保存到session后,跳转到价格列表页面(可手动输入,或者选择特定的价格列表项)

       微信、支付宝、UC浏览器扫码结果如下:(页面比例缩放了)

 

                 

微信、支付宝用户基本信息获取基本完成了,里面有好多坑,待一个个挖,一个个填。

--明天再写

三:订单管理

本demo业务模式有充值,代金券,所以下单时先判断代金券、余额信息,如都不足,则发起在线支付。

1.微信支付

a)统一下单:

 

public UnifiedorderResponse putUnifiedOrder(PageData pd) {
		UnifiedorderResponse unifiedorderResponse = null;
		UnifiedorderRequest unifiedorderRequest = new UnifiedorderRequest();
		unifiedorderRequest.setNonce_str(RandomStringGenerator.generate());// 随机字符串
		unifiedorderRequest.setBody("xxx自助按摩-" + pd.get("remark"));// 商品描述
		unifiedorderRequest.setOut_trade_no(pd.getString("order_number"));// 商户订单号
		Double three_pay_amount = Double.valueOf(pd.getString("three_pay_amount"));
		Double pay_money = BigDemicalUtil.mul(three_pay_amount, new Double(100));
		int total_free = new Double(pay_money).intValue();
		unifiedorderRequest.setTotal_fee(total_free); // 钱,单位是分
		//unifiedorderRequest.setTotal_fee(1); // 钱,单位是分
		unifiedorderRequest.setSpbill_create_ip(pd.getString("add_ip"));// 终端IP
		unifiedorderRequest.setTrade_type("JSAPI");// 交易类型
		unifiedorderRequest.setOpenid(pd.getString("openid"));// 用户标识
		unifiedorderRequest.setNotify_url(WXConstants.PAY_CALLBACK_URL);// 通知地址,异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
		unifiedorderRequest.setTime_start(pd.getString("timeStart"));// 交易起始时间
		try {
			unifiedorderResponse = PayManager.unifiedorder(unifiedorderRequest);
		} catch (SignatureException e) {
			e.printStackTrace();
		} catch (PayApiException e) {
			e.printStackTrace();
		} catch (PayBusinessException e) {
			e.printStackTrace();
		}
		return unifiedorderResponse;
	}

b)下单工具类PayManager

 

 

public class PayManager {

    private static Logger logger = Logger.getLogger(PayManager.class);

    /**
     * 统一下单
     */
    private static final String HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
     * 查询订单
     */
    private static final String HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
    /**
     * 关闭订单
     */
    private static final String HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_CLOSEORDER = "https://api.mch.weixin.qq.com/pay/closeorder";
    /**
     * 申请退款
     */
    private static final String HTTPS_API_MCH_WEIXIN_QQ_COM_SECAPI_PAY_REFUND = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    /**
     * 查询退款
     */
    private static final String HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_REFUNDQUERY = "https://api.mch.weixin.qq.com/pay/refundquery";
    /**
     * 下载对账单
     */
    private static final String HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_DOWNLOADBILL = "https://api.mch.weixin.qq.com/pay/downloadbill";
    /**
     * 测速上报
     */
    private static final String HTTPS_API_MCH_WEIXIN_QQ_COM_PAYITIL_REPORT = "https://api.mch.weixin.qq.com/payitil/report";

    /**
     * 统一下单
     * <p>参考<a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1">开发文档</p>
     *
     * @param request
     * @return
     * @throws SignatureException
     * @throws PayApiException
     * @throws PayBusinessException
     */
    public static UnifiedorderResponse unifiedorder(UnifiedorderRequest request) throws SignatureException, PayApiException, PayBusinessException {
        JaxbParser requestParser = buildJAXBParser(UnifiedorderRequest.class);
        JaxbParser responseParser = buildJAXBParser(UnifiedorderResponse.class);
        request.setSign(signature(request));
        String postData = requestParser.toXML(request);
        logger.info("post data \n" + postData);
        String postResult = post(HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_UNIFIEDORDER, postData);
        logger.info("post result \n" + postResult);
        checkAccess(postResult);
        checkBusiness(postResult);
        validResponseSign(postResult);
        UnifiedorderResponse response = (UnifiedorderResponse) responseParser.toObj(postResult);
        return response;
    }

    /**
     * 查询订单
     * <p>参考<a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2">开发文档</p>
     *
     * @param request
     * @return
     * @throws SignatureException
     * @throws PayApiException
     * @throws PayBusinessException
     */
    public static OrderqueryResponse orderquery(OrderqueryRequest request) throws SignatureException, PayApiException, PayBusinessException {
        JaxbParser requestParser = buildJAXBParser(OrderqueryRequest.class);
        JaxbParser responseParser = buildJAXBParser(OrderqueryResponse.class);
        request.setSign(signature(request));
        String postData = requestParser.toXML(request);
        logger.info("post data \n" + postData);
        String postResult = post(HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_ORDERQUERY, postData);
        logger.info("post result \n" + postResult);
        checkAccess(postResult);
        checkBusiness(postResult);
        validResponseSign(postResult);
        OrderqueryResponse response = (OrderqueryResponse) responseParser.toObj(postResult);
        try {
            parseCouponsForOrderquery(postResult, response);
        } catch (Exception e) {
            logger.error("解析代金券或立减优惠失败", e);
            PayApiException exception = new PayApiException(PayCode.FAIL, "解析代金券或立减优惠失败");
            throw exception;
        }
        return response;
    }

    /**
     * 关闭订单
     * <p>参考<a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2">开发文档</p>
     *
     * @param request
     * @return
     * @throws SignatureException
     * @throws PayApiException
     * @throws PayBusinessException
     */
    public static CloseorderResponse closeorder(CloseorderRequest request) throws SignatureException, PayApiException, PayBusinessException {
        JaxbParser requestParser = buildJAXBParser(CloseorderRequest.class);
        JaxbParser responseParser = buildJAXBParser(CloseorderResponse.class);
        request.setSign(signature(request));
        String postData = requestParser.toXML(request);
        logger.info("post data \n" + postData);
        String postResult = post(HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_CLOSEORDER, postData);
        logger.info("post result \n" + postResult);
        checkAccess(postResult);
        checkBusiness(postResult);
        validResponseSign(postResult);
        CloseorderResponse response = (CloseorderResponse) responseParser.toObj(postResult);
        return response;
    }

    /**
     * 申请退款
     * <p>参考<a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4">开发文档</p>
     *
     * @param request
     * @return
     * @throws SignatureException
     * @throws PayApiException
     * @throws PayBusinessException
     */
    public static RefundResponse refund(RefundRequest request) throws SignatureException, PayApiException, PayBusinessException {
        JaxbParser requestParser = buildJAXBParser(RefundRequest.class);
        JaxbParser responseParser = buildJAXBParser(RefundResponse.class);
        request.setSign(signature(request));
        String postData = requestParser.toXML(request);
        logger.info("post data \n" + postData);
        String postResult = post(HTTPS_API_MCH_WEIXIN_QQ_COM_SECAPI_PAY_REFUND, postData);
        logger.info("post result \n" + postResult);
        checkAccess(postResult);
        checkBusiness(postResult);
        validResponseSign(postResult);
        RefundResponse response = (RefundResponse) responseParser.toObj(postResult);
        return response;
    }

    /**
     * 查询退款
     * <p>参考<a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4">开发文档</p>
     *
     * @param request
     * @return
     * @throws SignatureException
     * @throws PayApiException
     * @throws PayBusinessException
     */
    public static RefundqueryResponse refundquery(RefundqueryRequest request) throws SignatureException, PayApiException, PayBusinessException {
        JaxbParser requestParser = buildJAXBParser(RefundqueryRequest.class);
        JaxbParser responseParser = buildJAXBParser(RefundqueryResponse.class);
        request.setSign(signature(request));
        String postData = requestParser.toXML(request);
        logger.info("post data \n" + postData);
        String postResult = post(HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_REFUNDQUERY, postData);
        logger.info("post result \n" + postResult);
        checkAccess(postResult);
        checkBusiness(postResult);
        validResponseSign(postResult);
        RefundqueryResponse response = (RefundqueryResponse) responseParser.toObj(postResult);
        try {
            parseCouponsForRefundquery(postResult, response);
        } catch (Exception e) {
            logger.error("解析代金券或立减优惠失败", e);
            PayApiException exception = new PayApiException(PayCode.FAIL, "解析代金券或立减优惠失败");
            throw exception;
        }
        return response;
    }

    /**
     * 下载对账单
     * <p>参考<a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6">开发文档</p>
     *
     * @param request
     * @return
     * @throws PayApiException
     */
    public static String downloadbill(DownloadbillRequest request) throws PayApiException {
        JaxbParser requestParser = buildJAXBParser(DownloadbillRequest.class);
        JaxbParser responseParser = buildJAXBParser(PayApiException.class);
        request.setSign(signature(request));
        String postData = requestParser.toXML(request);
        logger.info("post data \n" + postData);
        String postResult = post(HTTPS_API_MCH_WEIXIN_QQ_COM_PAY_DOWNLOADBILL, postData);
        logger.info("post result \n" + postResult);
        PayApiException exception = null;
        try {
            Map<String, Object> mapFromXMLString = getMapFromXMLString(postResult);
            exception = new PayApiException(mapFromXMLString.get("return_code").toString(), mapFromXMLString.get("return_msg").toString());
        } catch (Exception e) {
            // 如果不是XML则说明对账单下载成功
        }
        if (exception != null) {
            throw exception;
        } else {
            return postResult;
        }
    }

    /**
     * 封装支付结果通知
     * <p/>
     * <b>注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 </b>
     * <p><a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7">开发文档</p>
     *
     * @param servletRequest
     * @return
     * @throws SignatureException
     * @throws PayApiException
     * @throws PayBusinessException
     */
    public static PayResultNotifyResponse parsePayResultNotify(ServletRequest servletRequest, ServletResponse servletResponse) throws SignatureException, PayApiException, PayBusinessException {
        JaxbParser responseParser = buildJAXBParser(PayResultNotifyResponse.class);
        JaxbParser exceptionParser = buildJAXBParser(PayApiException.class);
        PayApiException exception = new PayApiException(PayCode.SUCCESS, "OK");
        String postResult;
        try {
            int len;
            byte[] b = new byte[1024];
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            InputStream servletInputStream = servletRequest.getInputStream();
            while ((len = servletInputStream.read(b)) != -1) {
                stream.write(b, 0, len);
            }
            postResult = stream.toString(Consts.UTF_8.name());
        } catch (IOException e) {
            logger.error("支付结果通知数据解析失败", e);
            exception = new PayApiException(PayCode.FAIL, "支付结果通知数据解析失败");
            responseToWechat(servletResponse, exceptionParser.toXML(exception));
            throw exception;
        }
        logger.info("result data \n" + postResult);
        checkAccess(postResult);
        try {
            validResponseSign(postResult);
        } catch (SignatureException e) {
            exception = new PayApiException(PayCode.FAIL, "签名校验失败");
            responseToWechat(servletResponse, exceptionParser.toXML(exception));
            throw e;
        }
        checkBusiness(postResult);
        PayResultNotifyResponse response = (PayResultNotifyResponse) responseParser.toObj(postResult);
        try {
            parseCouponsForPayResultNotify(postResult, response);
        } catch (Exception e) {
            logger.error("解析代金券或立减优惠失败", e);
            exception = new PayApiException(PayCode.FAIL, "解析代金券或立减优惠失败");
            responseToWechat(servletResponse, exceptionParser.toXML(exception));
            throw exception;
        }
        responseToWechat(servletResponse, exceptionParser.toXML(exception));
        return response;
    }

    /**
     * 商户处理支付结果通知后同步返回给微信参数
     *
     * @param servletResponse
     * @param postData
     * @throws PayApiException
     */
    private static void responseToWechat(ServletResponse servletResponse, String postData) throws PayApiException {
        try {
            servletResponse.getOutputStream().write(postData.getBytes(Consts.UTF_8));
            servletResponse.getOutputStream().flush();
            servletResponse.getOutputStream().close();
        } catch (IOException e) {
            throw new PayApiException(PayCode.FAIL, "支付结果通知同步返回失败");
        }
    }

    /**
     * 构造H5调用支付的参数对象
     *
     * @param timeStamp
     * @param nonceStr
     * @param prepayId
     * @return
     */
    public static H5PayParam buildH5PayConfig(String timeStamp, String nonceStr, String prepayId) {
        H5PayParam config = new H5PayParam();
        config.setTimeStamp(timeStamp);
        config.setNonceStr(nonceStr);
        config.setPackageWithPrepayId("prepay_id=" + prepayId);
        config.setPaySign(signature(config));
        return config;
    }
}


c)微信前台支付页面

 

 

//提交订单
 	 $('#pay_btn').click(function () {
		$('#pay_btn').val("提交中...");
		$('#pay_btn').attr("disabled","disabled");
		var wechatInfo = navigator.userAgent.match(/MicroMessenger\/([\d\.]+)/i) ;  
		if ( wechatInfo[1] < "5.0" ) { 
			toastr.warning("请升级微信至5.0以上版本!");
			return ;
		}
		var coupon = $("#coupon").val();
		var type=${type};
		isused = $(this).find("a").hasClass("active");
        $.ajax({  
            url: '<%=basePath%>/order/payOrder.do', 
            data: {isused:isused,type:type,coupon:coupon},
            type: 'POST',  
            cache:false,   
           // async:false,   
            dataType: 'JSON',  
            timeout: 5000,  
            error: function(textStatus){
            	$('#pay_btn').removeAttr("disabled");
    			$('#pay_btn').val("立即支付");
            	toastr.error('信息错误,请重试!');
            	},  
            success: function(result){  
            	$('#pay_btn').removeAttr("disabled");
    		$('#pay_btn').val("立即支付");
    		var data=eval(result);
    		data=data[0];
    		var wx_config=eval(data.config);
    		appId = wx_config.appid;  
                timeStamp = wx_config.timeStamp;    
                nonceStr = wx_config.nonceStr;  
                pg = wx_config.packageWithPrepayId;  
                signType = wx_config.signType;   
                paySign = wx_config.paySign;
                order_number=data.orderNumber;
                //唤起微信支付 
            	pay();
            }  
        }); 
  	});
 	 
 	//唤起微信支付  
 	function pay(){    
 	    if (typeof WeixinJSBridge == "undefined"){    
 	       if( document.addEventListener ){    
 	           document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);    
 	       }else if (document.attachEvent){    
 	           document.attachEvent('WeixinJSBridgeReady', onBridgeReady);     
 	           document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);    
 	       }    
 	    }else{    
 	       onBridgeReady();    
 	    }     
 	}   
 	
 	//开始支付  
 	function onBridgeReady(){    
 	    WeixinJSBridge.invoke(    
 	        'getBrandWCPayRequest', {    
 	            "appId" : appId,     //公众号名称,由商户传入         
 	            "timeStamp": timeStamp+"",         //时间戳,自1970年以来的秒数         
 	            "nonceStr" : nonceStr, //随机串         
 	            "package" : pg,         
 	            "signType" : signType,         //微信签名方式:         
 	            "paySign" : paySign    //微信签名     
 	        },    
 	        function(res){         
 	            if(res.err_msg == "get_brand_wcpay_request:ok" ) {    
 	                //回到用户订单列表  
 	                window.location.href="xxx/paySuccess.do?orderNumber="+order_number;  
 	            }else if (res.err_msg == "get_brand_wcpay_request:cancel")  {  
 	                //alert("支付过程中用户取消");  
 	            }else{  
 	               //支付失败  
 	               //alert(res.err_msg)  
 	            }       
 	        }    
 	    );     
 	 }  

d)效果图(前端使用了toastr通知插件)

 

三:支付宝支付

 

a)支付宝下单

 

if (Constants.ALIPAY.equals(appType)) {// 支付宝
    Map<String, String> aliPayMap = new HashMap<String, String>();
    aliPayMap.put("orderNumber", orderNumber);// 商户订单号
    aliPayMap.put("subject", "xxx自助按摩-" + remark);// 商品的标题/交易标题/订单标题/订单关键字等
    aliPayMap.put("amount", three_pay_amount + "");// 订单总金额,单位为元,精确到小数点后两位
    aliPayMap.put("boby", Constants.getPriceTypeLongMsg().get(type));// 对一笔交易的具体描述信息。
    aliPayMap.put("returnUrl", "xxx/paySuccess.do?orderNumber=" + orderNumber);// 对一笔交易的具体描述信息。
    // form表单生产
    String form = alipayService.aliPay(aliPayMap, basePath);
    response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
    response.getWriter().write(form);// 直接将完整的表单html输出到页面
    response.getWriter().flush();
    response.getWriter().close();
}

b)下单工具类

 

public String aliPay(Map<String, String> map, String basePath) {
		String orderNumber = map.get("orderNumber");
		String subject = map.get("subject");
		String amount = map.get("amount");
		String boby = map.get("boby");
		String returnUrl = map.get("returnUrl");
		AlipayTradeWapPayRequest alipay_request = new AlipayTradeWapPayRequest();
		// 封装请求支付信息
		AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
		model.setOutTradeNo(orderNumber);// 商户订单号
		model.setSubject(subject);// 商品的标题/交易标题/订单标题/订单关键字等
		model.setTotalAmount(amount);// 订单总金额,单位为元,精确到小数点后两位
		//model.setTotalAmount(0.01 + "");// 订单总金额,单位为元,精确到小数点后两位
		model.setBody(boby);// 对一笔交易的具体描述信息
		model.setTimeoutExpress("10m");// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d
		model.setProductCode("QUICK_WAP_PAY");// 销售产品码,商家和支付宝签约的产品码。该产品请填写固定值:QUICK_WAP_WAY
		alipay_request.setBizModel(model);
		// 设置异步通知地址
		alipay_request.setNotifyUrl(AlipayConfig.NOTIFY_URL);
		// 设置同步地址
		alipay_request.setReturnUrl(returnUrl);
		// form表单生产
		String form = "";
		try {
			// 调用SDK生成表单
			form = alipayClient.pageExecute(alipay_request).getBody();
		} catch (AlipayApiException e) {
			e.printStackTrace();
		}
		return form;
	}

c)效果

 

                     
 

四:订单页面

五:收入明细

六:收入统计

七:代金券

八:余额充值

九:微信中控服务器

10:mysql5.7 保存微信emoj表情

11.redis数据库的使用

12.短信网关、邮件系统

13.系统部署在阿里云上

14.域名的备案

15.微信重复请求

and so on...

   3个月的撸码,回头过来,能写的东西太多,垮过的坑更多,忙着年终总结+发奖金,没空写了,先开个头,get系统源码发邮件至 540769049@qq.com,备注来由。

  • 3
    点赞
  • 13
    收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 2

打赏作者

gotohomebye

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值