您的位置:首页 > 移动开发 > 微信开发

“一码多付”,微信支付、支付宝支付,微信中控服务器

2018-01-08 15:10 706 查看

  
  写着写着,越写越多,思绪写不下了,回头写在前面的话。此系统属个人创业项目,历时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", userinfoSh
f5bb
areResponse);
// 保存用户信息
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个月的撸码,回头过来,能写的东西太多,垮过的坑更多,忙着年终总结+发奖金,没空写了,先开个头,有疑问的随时沟通交流吧,qq:540769049,备注来由。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息