微信支付回调通知查漏补缺篇

本文讲述了因代码不规范和业务需求变化,对微信支付回调通知进行改造的原因,强调了回调通知验证与并发控制的重要性,并分享了部分关键代码。
摘要由CSDN通过智能技术生成

    前段时间公司交给了我一个微信支付相关的业务功能改造任务,那么为什么要改造呢?
    主要原因有两个:

    其一. 代码不规范、不够严谨,支付漏洞多,导致系统被黑客攻击,公司亏钱;

    其二. 业务需求变动,之前的需求是会员支付完成后的结算都是走的虚拟账户余额,
    现在的需求是在会员支付完成后对会员进行实时部分退款(消费返现)并且公司向会员支付的
    商家实时打款(转账,相当于给商家的货款,因为商家都在公司平台上入驻的);
    那么改动最大得部分无非就是回调通知函数。

    特别提醒:

    1、商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏      导致出现“假通知”,造成资金损失。

   2、当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果       处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据     混乱。

    贴出部分精华代码:

package com.sam.project.mvc.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.sam.project.mvc.common.log.RequestLog;
import com.sam.project.mvc.common.manager.RyscSecurityManager;
import com.sam.project.mvc.model.*;
import com.sam.project.mvc.service.LocalLifeAccountService;
import com.sam.project.mvc.service.LoclifeAccountDetailService;
import com.sam.project.mvc.service.MemberService;
import com.sam.project.mvc.service.NearbyService;
import com.sam.project.mvc.service.OrderService;
import com.sam.project.mvc.service.RyMiddleSettlementService;
import com.sam.project.mvc.service.RyscBusinessProfitService;
import com.sam.project.mvc.util.HttpRequest;
import com.sam.project.mvc.util.JavaAndPhp;
import com.sam.project.mvc.util.RSASecurityCoder;
import com.sam.project.mvc.util.SecurityUtil;
import com.sam.project.mvc.util.sdk.WXPayUtil;
import com.sam.project.mvc.util.wexin.WeXinUtil;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Controller
@RequestMapping("/businessProfit")
public class RyscBusinessProfitController extends BaseController {
	private static final Logger LOG = LoggerFactory.getLogger(RequestLog.class);
	private static final String mchId = "xxxxxxxxxxxx";
	private static ExecutorService executorService;
	@Autowired
	private RyscBusinessProfitService ryscBusinessProfitService;
	@Autowired
	private NearbyService nearbyService;
	@Autowired
	private OrderService orderService;
	@Autowired
	private RyMiddleSettlementService ryMiddleSettlementService;
	@Autowired
	private LocalLifeAccountService localLifeAccountService;
	@Autowired
	private LoclifeAccountDetailService loclifeAccountDetailService;
	@Autowired
	private MemberService memberService;

	private Lock accountLock = new ReentrantLock();

	private FutureTask<Integer> asaFutureTask2 = null;
	private FutureTask<Integer> asaFutureTask = null;
	private FutureTask<Integer> asaFutureTask4 = null;
	private FutureTask<Integer> asaFutureTask6 = null;
	private FutureTask<String> asaFutureTask8 = null;

	private FutureTask<Integer> refundExceptionTask = null;
	private FutureTask<Integer> transfersExceptionTask = null;
	private FutureTask<Integer> inviteCarveUpExceptionTask = null;
	
	@SuppressWarnings("unused")
	@RequestMapping(value = "payBackBusinessProfit", method = RequestMethod.POST)
	@ResponseBody
	public String payBackBusinessProfit(@RequestBody String payBack) {
		String resXml = "";
		// 加锁处理本地生活虚拟账户中的金额
		accountLock.lock();
		try {
			executorService = Executors.newCachedThreadPool();// 创建线程池
			refundExceptionTask = null;
			transfersExceptionTask = null;
			inviteCarveUpExceptionTask = null;
			Map<Object, Object> map;
			map = WXPayUtil.xmlToMap2(payBack);
			String out_trade_no = map.get("out_trade_no").toString();
			float userSendMoney = Float.parseFloat(map.get("total_fee").toString()) / 100;// 回调返回的金额
			if (map != null) {
				LOG.info("======================================");
				logger.info("请求回调后的参数:{}", map);
				System.out.println(map.get("sign"));
				Boolean flag = verifyWeixinNotify(map);

				LOG.info("map:{}" + map);

				LOG.info("======================================");
				if (flag) {
					// 验证签名成功
					RySettementAction action = new RySettementAction();
					RyBussinessOrder rbo = nearbyService.findByWxOrderId(out_trade_no);
					Integer maxMidId = null;
					if (rbo != null) {
						maxMidId = ryMiddleSettlementService.getMidIdByOrderId(rbo.getId());
					}
					String orderDesc = "";

					if (rbo == null) {
						System.out.println("查询数据库订单失败,没有这个订单。");
						orderDesc = "查询数据库订单失败,没有这个订单。";
						return resXml;
					}

					// 查询商家信息
					RyNearbyBusiness rnb = nearbyService.findRyNearbyBusinessById(rbo.getBussinessId());
					// 异步写入结算流水日志
					addSettementActionTask(action, maxMidId, rnb.getLeaderId());

					if (rbo.getIsPay() == 1 && rbo.getIsNotice() == 1) {
						// 已经支付并通知成功
						resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
								+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml>";

						return resXml;
					}
					String oId = rbo.getOrderId();
					int orderId = rbo.getId();

					// 加密字符串对比并验证金额
					String tempKeyMoney = "";
					String tempKey = oId + "," + rbo.getOrderMoney();

					System.out.println("对比、加密前字符串:" + tempKey);
					RyParameter ryParameter = nearbyService.getRyParameter(4);// 请求参数秘钥

					String keyWord = JavaAndPhp.encrypt(tempKey, ryParameter.getParameterValue());// 现在加密的字符串
					String attach = map.get("attach").toString(); // 商家数据包,原样返回(预支付加密的字符串)
					String orderPassword = rbo.getOrderPassword(); // 数据库保存加密字符串

					String code = "";

					RyscBusinessProfit rbp = new RyscBusinessProfit();
					rbp.setMemberId(rbo.getCreateMemberId());
					rbp.setBusinessPhone(rnb.getBusinessPhone());
					float businessProfit = Float.parseFloat(rbo.getOrderMoney()) * rnb.getBusinessDiscount();
					DecimalFormat fnum = new DecimalFormat("##0.00");
					String temp = fnum.format(businessProfit);
					rbp.setBusinessProfit(Float.parseFloat(temp));

					float memberReward = Float.parseFloat(rbo.getOrderMoney()) * rnb.getBusinessMoneyrewardDiscount();
					String temp2 = fnum.format(memberReward);
					rbp.setMemberReward(Float.parseFloat(temp2));

					rbp.setOId(rbo.getOrderId());
					rbp.setOrderId(rbo.getId());

					float orderMoney = Float.parseFloat(rbo.getOrderMoney());

					// 查询订单
					org.json.JSONObject jsonObject = orderService.checkWxOrderResult(rbo.getCreateMemberId(),
							rbo.getOrderId(), null);

					if (jsonObject == null) {
						System.out.println("查询微信订单失败,没有这个订单。");
						orderDesc = "查询微信订单失败,没有这个订单。";
					} else {
						System.out.println(jsonObject.toString());
						code = jsonObject.getString("trade_state");
					}

					boolean orderStatus = false;
					int isPay = 0;
					float inviteMoney = queryInviteMoney(rbo, orderMoney); // 会员上/上上级、商家上级分成
					switch (code) {
					case "SUCCESS":
						orderDesc = "查询微信订单,支付成功!金额还未比对";
						// 1、比对金额
						String keyWordDecrypt = JavaAndPhp.decrypt(keyWord, ryParameter.getParameterValue()); // 解密现在加密的字符串
						String attachDecrypt = JavaAndPhp.decrypt(attach, ryParameter.getParameterValue()); // 解密预支付加密的字符串
						String orderPasswordDecrypt = JavaAndPhp.decrypt(orderPassword,
								ryParameter.getParameterValue());// 解密数据库加密的字符串

						float keyWordMoney = Float.parseFloat(keyWordDecrypt.split(",")[1]);
						float attachMoney = Float.parseFloat(attachDecrypt.split(",")[1]);
						float orderPasswordMoney = Float.parseFloat(orderPasswordDecrypt.split(",")[1]);

						float wxMoney = Float.parseFloat(jsonObject.getString("total_fee")); // 查询订单返回金额
						wxMoney = wxMoney / 100;
						String OrderEndTiem = jsonObject.getString("time_end");
						/**
						 * 1判断订单查询出来的金额,微信订单金额,乘以商家的百分比,跟前端传过来的商家打款金额对比。
						 * 2、该订单的支付
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值