现在的位置: 首页 > 综合 > 正文

对易宝支付的认识与总结

2018年05月14日 ⁄ 综合 ⁄ 共 10505字 ⁄ 字号 评论关闭

        上周,有一个任务是实现考生付款的功能,当时接到这个功能时,把他想简单了,只想到我和考生之间的交互了,连银行都忘了,哈哈!于是觉得做出来很容易嘛!但是,再去做时,发现并不是那么回事,因为付款是一件很神圣的事,参与的对象有考生,我,银行,第三方,毕竟涉及到钱了嘛!这里的我就代表我公司了,最终收到钱的对象!人家付款都觉得有点麻烦,何况咱写程序的呢,也得给他造点麻烦,以免考生的钱花错地方了呀,那人家还怪咱呢,咱怪谁呢?于是咱就要委屈自己了,要多费点脑子,少玩一会了!后来去了解了一下易宝支付,才知道,他就是要介入考生的那个”小三“!

        当然,这个第三方介入与不介入也都行!付款的方式有好几种呢:

1.考生把钱直接送到咱手里

2.考生把钱打给银行,银行再打给咱

3.考生把钱给第三方,第三方再跟银行交互,银行再打给咱

那么,这里一共有三种,为什么要选择第三种呢?

第一种,当然虽然直接,但是要是考生在海南,公司在北京,难道考生坐飞机来北京送到公司里?当然不现实,pass!

第二种,方便了考生,也方便了公司,对于大额交易,安全啊!但是呢,公司系统直接与银行对接,要是银行系统更新了呢,那咱不也要改点什么,咱是被人牵着鼻子走啊,而且人家问你要的钱多啊,你爱接不接,咱能不接吗?那就先不说了,另一方面,什么农业银行啊,工商银行啊,交通银行啊,发展银行啊,这么多的银行,你都去接,那你收考生的钱直接就给银行吧,太贵了,太麻烦了!

所以啊,综合来看,我们只有采取第三种方式才划算了点,为什么划算了点呢?咱们直接与第三方对接,对接免费啊,不过人家收1%手续费,那咱交的不多,咱的交易额不大啊,要是咱交易个上亿的,咱还不用他了呢,是吧,没有银行安全啊!

好了,废话不多说了,下面咱来学习学习,认识认识这个神奇的很多很多商家在用的易宝支付吧!


首先,我们再来了解易宝支付这个名词:易宝支付

然后,我们去下载易宝支付的jar包和帮助文档:易宝支付帮助文档和jar包

当然,要想测试你的易宝支付功能,你需要去易宝申请商户编号和商家密钥!

(易宝支付基本上都是采取gbk或gb2312编码格式)

下面看看我的支付流程:

<1>.考生应交费信息页面:


<2>.点击“易宝支付”,在后台要处理的代码是:

ReportAction里面pay方法:

public ActionForward pay(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 从request中获取session对象
		HttpSession session = request.getSession();
		// 从session中获取examinee对象
		Examinee examinee = (Examinee) session.getAttribute("_EXAMINEE");
		// 从examinee对象中获取examineeid
		Long examineeid = examinee.getExamineeId();
		Map map = rm.readyToPay(examineeid);
		String message = (String) map.get("message");
		if (message != null && !message.equals("")) {
			response.sendRedirect(message);
		} else {
			message = "你已缴费!请勿重复交费!";
			request.setAttribute("message", message);
			return mapping.findForward("prompt");
		}
		return null;
	}


ReportManagerImpl里面readyToPay方法:

	public Map readyToPay(Long examineeid) throws Exception {
		// 1.校验基准账户
		List list = reportDAO.selectAccount();
		if (list.size() == 0) {
			message = "基准账户还未维护,请联系管理员!";
			throw new AppException(message);
		} else if (list.size() > 1) {
			message = "基准账户存在多个账户,请联系管理员!";
			throw new AppException(message);
		}
		// 2.锁定账户
		// reportDAO.lockUserInfo(examineeid);
		// 3.校验收费标准
		list = reportDAO.selectAmount();
		if (list.size() == 0) {
			message = "不存在收费标准,请联系管理员!";
			throw new AppException(message);
		} else if (list.size() > 1) {
			message = "收费标准有多条,请联系管理员!";
			throw new AppException(message);
		}
		// 4.校验订单是否有效
		map = reportDAO.selectCharge(examineeid);
		Long chargeid = 0L;
		if (map != null) {
			// 取出验证考生订单的字段信息charge_id
			Object ochargeid = (Object) map.get("charge_id");
			chargeid = Long.parseLong(ochargeid.toString());
		}
		// 订单不存在的情况
		if (chargeid == 0 || reportDAO.selectCharge(examineeid) == null || map == null) {
			// 生成新订单
			int result = reportDAO.insertCharge(examineeid);
			if (result == 1) {
				// 调用接口交费
				String message = this.toPay(examineeid);
				map.put("message", message);
			}
		} else {
			// 订单存在
			map = reportDAO.selectCharge(examineeid);
			// 取出验证考生是否已经交费的字段信息
			String signzf = (String) map.get("sign_zf");
			String signqr = (String) map.get("sign_qr");

			// 验证考生是否已经交费成功
			if (signzf.equals("0") && signqr.equals("1")) {
				message = "你已缴费!请勿重复交费!";
				map.put("message", message);
			} else {
				map = this.queryStatus(examineeid);
				// 放进一个message键,便于action中判断
				map.put("message", "");
				String status = (String) map.get("status");
				if (!"SUCCESS".equals(status)) {
					if ("CANCELED".equals(status)) {
						// 订单被取消,先作废原订单,再重新生成一条新的订单信息并插入
						String orderno = (String) map.get("orderno");
						orderno = null;
						// 取消订单
						reportDAO.cancelChargeOrder(chargeid);
						// 取消订单后生成新订单
						reportDAO.insertCharge(examineeid);
					}
					// 未支付成功,需要调用易宝的支付接口进行支付操作
					message = this.toPay(examineeid);
					map.put("message", message);
				}
			}
		}

		return map;
	}


ReportManagerImpl里面toPay方法:

public String toPay(Long examineeid) {
		List list = (List) reportDAO.selectAccount();
		map = reportDAO.selectPayInfo(examineeid);
		Map maps = (Map) list.get(0);
		// 商户密钥
		String keyValue = (String) maps.get("yeepay_key");
		// 网上支付请求地址
		String onlinePaymentReqURL = Configuration.getInstance().getValue("onlinePaymentReqURL");
		// 1. 业务类型
		String p0_Cmd = Configuration.getInstance().getValue("p0_Cmd");
		// 2. 商户编号
		String p1_MerId = (String) maps.get("yeepay_no");
		// 3. 商户订单号
		String p2_Order = (String) map.get("orderno");
		// 4. 支付金额
		String p3_Amt = map.get("amount").toString();
		// 5.交易币种
		String p4_Cur = Configuration.getInstance().getValue("p4_Cur");
		// 6.商品名称
		String p5_Pid = Configuration.getInstance().getValue("p5_Pid");
		// 7.商品种类
		String p6_Pcat = Configuration.getInstance().getValue("p6_Pcat");
		// 8.商品描述
		String p7_Pdesc = Configuration.getInstance().getValue("p7_Pdesc");
		// 9.商户接收支付成功数据的地址
		String p8_Url = Configuration.getInstance().getValue("p8_Url");
		// 10. 送货地址
		String p9_SAF = Configuration.getInstance().getValue("p9_SAF");
		// 11.商户扩展信息
		String pa_MP = "";
		// 12.银行编码
		String pd_FrpId = "";
		// 13. 应答机制
		String pr_NeedResponse = Configuration.getInstance().getValue("pr_NeedResponse");
		onlinePaymentReqURL = onlinePaymentReqURL.indexOf("?") == -1 ? onlinePaymentReqURL + "?" : onlinePaymentReqURL;
		// 获得加密Md5值-hmac
		String hmac = PaymentForOnlineService.getReqMd5HmacForOnlinePayment(p0_Cmd, p1_MerId, p2_Order, p3_Amt, p4_Cur, p5_Pid, p6_Pcat, p7_Pdesc, p8_Url, p9_SAF, pa_MP, pd_FrpId, pr_NeedResponse, keyValue);
		Map<String, String> mapUrl = new HashMap<String, String>();
		mapUrl.put("p0_Cmd", p0_Cmd);
		mapUrl.put("p1_MerId", p1_MerId);
		mapUrl.put("p2_Order", p2_Order);
		mapUrl.put("p3_Amt", p3_Amt);
		mapUrl.put("p4_Cur", p4_Cur);
		mapUrl.put("p5_Pid", p5_Pid);
		mapUrl.put("p6_Pcat", p6_Pcat);
		mapUrl.put("p7_Pdesc", p7_Pdesc);
		mapUrl.put("p8_Url", p8_Url);
		mapUrl.put("p9_SAF", p9_SAF);
		mapUrl.put("pa_MP", pa_MP);
		mapUrl.put("pd_FrpId", pd_FrpId);
		mapUrl.put("pr_NeedResponse", pr_NeedResponse);
		mapUrl.put("hmac", hmac);
		String payUrl = onlinePaymentReqURL + this.combinUrl(mapUrl);
		return payUrl;
	}


ReportManagerImpl里面queryStatus方法:

public Map queryStatus(Long examineeid) throws Exception {
		map = reportDAO.selectPayInfo(examineeid);
		// 调用易宝订单状态查询接口方法查询订单状态
		QueryResult result = PaymentForOnlineService.queryByOrder((String) map.get("orderno"));
		String hmac = result.getHmac();
		if (hmac == null) {
			message = "订单状态查询失败,请稍后再试或联系管理员!";
			throw new AppException(message);
		}
		// 支付状态
		String payStatus = result.getRb_PayStatus();
		// /订单是否存在
		Boolean exist = false;
		if ("1".equals(result.getR1_Code())) {
			exist = true;
			if (!"INIT".equals(payStatus) && !"AUTHORIZED".equals(payStatus) && !"CANCELED".equals(payStatus)
					&& !"SUCCESS".equals(payStatus)) {
				message = "支付接口异常,请联系管理员!";
				throw new AppException(message);
			}
		} else {
			payStatus = "";
		}
		map.put("exist", exist);
		map.put("status", payStatus);

		return map;
	}


ReportManagerImpl里面combinUrl方法:

	private String combinUrl(Map<String, String> map) {
		if (null == map || map.keySet().size() == 0) {
			return ("");
		}
		StringBuffer url = new StringBuffer();
		Set keys = map.keySet();
		for (Iterator i = keys.iterator(); i.hasNext();) {
			String key = String.valueOf(i.next());
			if (map.containsKey(key)) {
				Object val = map.get(key);
				String str = val != null ? val.toString() : "";
				try {
					str = URLEncoder.encode(str, "GBK");
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
				url.append(key).append("=").append(str).append("&");
			}
		}
		String URL = url.toString();
		if ("&".equals("" + URL.charAt(URL.length() - 1))) {
			URL = URL.substring(0, URL.length() - 1);
		}
		return URL;
	}

配置文件merchantInfo.properties:

#交易请求地址
onlinePaymentReqURL=https://www.yeepay.com/app-merchant-proxy/node
#查询地址
queryQueryReqURL=http://www.yeepay.com/app-merchant-proxy/controller.action
#业务类型 在线支付请求,固定值 ”Buy”
p0_Cmd=Buy
#商户编号
p1_MerId=xxxxxxxxxxxxxxxxxx
#密钥
keyValue=xxxxxxxxxxxxxxxx
#交易币种
p4_Cur=CNY
#商品名称 默认为""
p5_Pid=
#商品种类 默认为""
p6_Pcat=
#商品描述 默认为""
p7_Pdesc=
#商户接收支付成功数据的地址
p8_Url=http://192.168.1.103:8888/ears/ReportAction.do?method=payCall
#需要填写送货信息 0:不需要  1:需要
p9_SAF=0
#是否需要应答机制 0:不需要  1:需要
pr_NeedResponse=1
#订单有效期,默认单位为(天)
pm_Period=1
#是否是易宝支付平台的用户登录名
py_IsYeePayName=1
# 是否自动分帐
pc_AutoSplit=1
# 订单有效期的单位
pn_Unit=day


ReportAction里面payCall方法:

	public ActionForward payCall(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 从request中获取session对象
		HttpSession session = request.getSession();
		// 从session中获取examinee对象
		Examinee examinee = (Examinee) session.getAttribute("_EXAMINEE");
		// 从examinee对象中获取examineeid
		Long examineeid = examinee.getExamineeId();
		// 1.商户编号
		String p1_MerId = request.getParameter("p1_MerId");
		// 2.业务类型
		String r0_Cmd = request.getParameter("r0_Cmd");
		// 3.支付结果,1表示成功
		String r1_Code = request.getParameter("r1_Code");
		// 4.易宝交易流水号
		String r2_TrxId = request.getParameter("r2_TrxId");
		// 5.总金额
		String r3_Amt = request.getParameter("r3_Amt");
		// 6.交易币种
		String r4_Cur = request.getParameter("r4_Cur");
		// 7.商品名称 默认为""
		String r5_Pid = request.getParameter("r5_Pid");
		// 8.商户订单号
		String r6_Order = request.getParameter("r6_Order");
		// 9.商家扩展信息
		String r8_MP = request.getParameter("r8_MP");
		// 10.支付状态
		String rb_PayStatus = request.getParameter("rb_PayStatus");
		// 11.支付的银行
		String rb_BankId = request.getParameter("rb_BankId");
		// 12.银行订单号
		String ro_BankOrderId = request.getParameter("ro_BankOrderId");
		// 支付成功时间
		String rp_PayDate = request.getParameter("rp_PayDate");
		// 商家密钥
		String keyValue = Configuration.getInstance().getValue("keyValue");
		// 签名
		String hmac = request.getParameter("hmac");
		// 调用verifyCallback方法处理易宝返回来的数据,给出处理消息
		String message = PaymentForOnlineService.verifyCallback(hmac, p1_MerId, r0_Cmd, r1_Code, r2_TrxId, r3_Amt, r4_Cur, r5_Pid, r6_Order, r8_MP, rb_PayStatus, rb_BankId, ro_BankOrderId, rp_PayDate, keyValue);
		if (message.equals("支付成功!")) {
			message = "订单号为" + r6_Order + "的用户在" + rp_PayDate + "成功支付" + r3_Amt + "r4_Cur";
			int sum = rm.updateData(examineeid, rb_BankId, ro_BankOrderId, rp_PayDate);
			if (sum == 2) {
				message = message + ",订单已状态更新成功!";
			} else {
				message = message + ",订单已状态更新失败!";
			}

		}
		request.setAttribute("message", message);
		return mapping.findForward("prompt");
	}


<3>.发送支付请求结果成功连接到易宝支付页面为:



<4>.选择工商银行后,页面连接到银行页面为:


<5>.如果单笔订单查询考生已缴费,点击易宝支付出现提示页面:



说明:

1.jar包里面的类:


2.生成hmac时参数一定要严格按照帮助文档上的参数顺序。

3.读取merchantInfo.properties文件信息:(merchantInfo.properties文件操作)

public class Configuration {
	

	private static Object lock              = new Object();
	private static Configuration config     = null;
	private static ResourceBundle rb        = null;
	private static final String CONFIG_FILE = "merchantInfo";
	
	private Configuration() {
		rb = ResourceBundle.getBundle(CONFIG_FILE);
	}
	
	public static Configuration getInstance() {
		synchronized(lock) {
			if(null == config) {
				config = new Configuration();
			}
		}
		return (config);
	}
	
	public String getValue(String key) {
		return (rb.getString(key));
	}
}

简述整个支付流程:单笔订单查询和单笔退款流程都和其流程一样,无非一些参数值和判断有差异

支付与处理响应结果流程:

1.从数据库或者页面获取到订单中的一些信息与商家密钥一块作为参数调用PaymentForOnlineService

类中getReqMd5HmacForOnlinePayment(p0_Cmd, p1_MerId, p2_Order, p3_Amt, p4_Cur, p5_Pid, p6_Pcat, p7_Pdesc, p8_Url, p9_SAF, pa_MP, pd_FrpId, pr_NeedResponse, keyValue)方法获得hmac签名。


2.把这些参数和hmac签名与支付请求路径https://www.yeepay.com/app-merchant-proxy/node连接字符串(就是https://www.yeepay.com/app-merchant-proxy/node?p0_Cmd=& p1_MerId=。。。&hmac=格式)。


3.然后重定向response.sendRedirect(https://www.yeepay.com/app-merchant-proxy/node?p0_Cmd=& p1_MerId=。。。&hmac=)开始支付;


4.支付完成(完成不是成功)又重定向到当初你填的 p8_Url值这个路径去,当然,易宝传过来的还有很多参数(参见说明文档)包括hmac签名,获得传过来的参数调用PaymentForOnlineService类中verifyCallback(String
hmac, String p1_MerId, String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt,
String r4_Cur, String r5_Pid, String r6_Order, String r8_MP, String rb_PayStatus, String rb_BankId, String
ro_BankOrderId,String rp_PayDate, String keyValue)方法在这个方法中调用DigestUtil类中hmacSign(sValue.toString(), keyValue)方法返回新的hmac签名,与传进来的hmac值相比较,相等则合法继续根据其他返回参数给予相应提示,不相等就提示不合法!(sValue.toString()是传进来的所有参数连接成的字符串,keyValue是商家密钥)。

抱歉!评论已关闭.