1. 易宝支付
1.1. 易宝介绍
2005年4月7日,北京通融通资讯技术有限公司正式推出易宝支付电子支付平台。易宝支付是国内领先的第三方电子支付服务提供商,创新推出集成了互联网、手机、电话的综合性电子专业支付平台总部位于北京,在上海,广州,深圳,成都等地设有分公司,预计2010年易宝支付交易规模将超过50亿元人民币,合作签约的大型商家已经超过1万家。
战略合作伙伴包括IBM、工商银行、招商银行、建设银行、民生银行、VISA以及中国联通、上海电信等。
1.2. 易宝支付途径介绍
在线支付:银行卡支付,会员支付(虚拟帐户)
移动支付:手机银行卡充值 (替代预付卡、充值卡、游戏卡)
电话支付:不受互联网限制,随时随地可以通过电话支付
1.3. 易宝商户测试信息
易宝支付主页: http://www.yeepay.com/ 商家登陆名: [email protected] 商家编号:10000940764 商家密钥:w0P75wMZ203fr46r5i70V556WHFa94j14yW5J6vuh4yo3nRl5jsqF3c41677 |
2. 基于Servlet支付
把易宝提供的工具类放到自己的项目中,代码:
package com.yeepay;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class DigestUtil {
private static String encodingCharset = "UTF-8";
/**
* @param aValue
* @param aKey
* @return
*/
public static String hmacSign(String aValue, String aKey) {
byte k_ipad[] = new byte[64];
byte k_opad[] = new byte[64];
byte keyb[];
byte value[];
try {
keyb = aKey.getBytes(encodingCharset);
value = aValue.getBytes(encodingCharset);
} catch (UnsupportedEncodingException e) {
keyb = aKey.getBytes();
value = aValue.getBytes();
}
Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
for (int i = 0; i < keyb.length; i++) {
k_ipad[i] = (byte) (keyb[i] ^ 0x36);
k_opad[i] = (byte) (keyb[i] ^ 0x5c);
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
return null;
}
md.update(k_ipad);
md.update(value);
byte dg[] = md.digest();
md.reset();
md.update(k_opad);
md.update(dg, 0, 16);
dg = md.digest();
return toHex(dg);
}
public static String toHex(byte input[]) {
if (input == null)
return null;
StringBuffer output = new StringBuffer(input.length * 2);
for (int i = 0; i < input.length; i++) {
int current = input[i] & 0xff;
if (current < 16)
output.append("0");
output.append(Integer.toString(current, 16));
}
return output.toString();
}
/**
*
* @param args
* @param key
* @return
*/
public static String getHmac(String[] args, String key) {
if (args == null || args.length == 0) {
return (null);
}
StringBuffer str = new StringBuffer();
for (int i = 0; i < args.length; i++) {
str.append(args[i]);
}
return (hmacSign(str.toString(), key));
}
/**
* @param aValue
* @return
*/
public static String digest(String aValue) {
aValue = aValue.trim();
byte value[];
try {
value = aValue.getBytes(encodingCharset);
} catch (UnsupportedEncodingException e) {
value = aValue.getBytes();
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
return toHex(md.digest(value));
}
// public static void main(String[] args) {
// System.out.println(hmacSign("AnnulCard1000043252120080620160450.0http://localhost/SZXpro/callback.asp这4564868265473632445648682654736324511","8UPp0KE8sq73zVP370vko7C39403rtK1YwX40Td6irH216036H27Eb12792t"));
// }
}
2.1. 订单支付页面代码
<body> <h1>在线支付演示</h1> <!-- 填写支付的必要信息,支付金额可以修改是为了方便,同一个订单号不能重复测试--> <form action="<%=request.getContextPath()%>/PaySer" method="post"> 此次购物订单编号<input type="text" name="p2_Order" /><br> money<input type="text" name="p3_Amt" value="0.01"/><br> 工商银行 <input type="radio" value="ICBC-NET" name="pd_FrpId"> 建设银行 <input type="radio" value="CCB-NET" name="pd_FrpId"><br> <input type="submit" value="submit" /> <input type="hidden" value="pay" name="status"/> </form> </body> |
2.2. 支付参数获取加密
String status = request.getParameter("status"); // 具体参数查看支付中介API if (status.equals("pay")) { // 加密的密钥,商家在支付中介注册成功后中介提供,MD5+密药保证安全性 String keyValue = "w0P75wMZ203fr46r5i70V556WHFa94j14yW5J6vuh4yo3nRl5jsqF3c41677"; String p0_Cmd = formatString("Buy"); // 商家编号,支付成功后买家银行卡的钱会转到商家在易宝的帐号中 String p1_MerId = formatString("10000940764"); String p2_Order = formatString(request.getParameter("p2_Order")); String p3_Amt = formatString(request.getParameter("p3_Amt")); String p4_Cur = formatString("CNY"); // 如果有注意中文转码 String p5_Pid = ""; String p6_Pcat = ""; String p7_Pdesc = ""; // 支付成功的回调地址, 成功后需要发送邮件、修改订单状态..... String p8_Url = "http://za00037.u01.netjsp.com/OnlinePay/PaySer?status=success"; String p9_SAF = "0"; String pa_MP = ""; String pd_FrpId = formatString(request.getParameter("pd_FrpId")); pd_FrpId = pd_FrpId.toUpperCase(); String pr_NeedResponse = "0"; String hmac = formatString(""); // 对明文进行 StringBuffer infoBuffer = new StringBuffer(); infoBuffer.append(p0_Cmd); infoBuffer.append(p1_MerId); infoBuffer.append(p2_Order); infoBuffer.append(p3_Amt); infoBuffer.append(p4_Cur); infoBuffer.append(p5_Pid); infoBuffer.append(p6_Pcat); infoBuffer.append(p7_Pdesc); infoBuffer.append(p8_Url); infoBuffer.append(p9_SAF); infoBuffer.append(pa_MP); infoBuffer.append(pd_FrpId); infoBuffer.append(pr_NeedResponse); // 加密后的密文存储到了hmac中 hmac = DigestUtil.hmacSign(infoBuffer.toString(), keyValue); // 把明文和密文都存储到request.setAttribute中 request.setAttribute("p0_Cmd", p0_Cmd); request.setAttribute("p1_MerId", p1_MerId); request.setAttribute("p2_Order", p2_Order); request.setAttribute("p3_Amt", p3_Amt); request.setAttribute("p4_Cur", p4_Cur); request.setAttribute("p5_Pid", p5_Pid); request.setAttribute("p6_Pcat", p6_Pcat); request.setAttribute("p7_Pdesc", p7_Pdesc); request.setAttribute("p8_Url", p8_Url); request.setAttribute("p9_SAF", p9_SAF); request.setAttribute("pa_MP", pa_MP); request.setAttribute("pd_FrpId", pd_FrpId); request.setAttribute("pr_NeedResponse", pr_NeedResponse); // 加密后的密文 request.setAttribute("hmac", hmac); request.getRequestDispatcher("/reqpay.jsp").forward(request, response); |
2.3. 支付提交页面
<form name="yeepay" action='https://www.yeepay.com/app-merchant-proxy/node' method='POST' target="_blank"> <input type='hidden' name='p0_Cmd' value='${requestScope.p0_Cmd}'> <input type='hidden' name='p1_MerId' value='${requestScope.p1_MerId}'> <input type='hidden' name='p2_Order' value='${requestScope.p2_Order}'> <input type='hidden' name='p3_Amt' value='${requestScope.p3_Amt}'> <input type='hidden' name='p4_Cur' value='${requestScope.p4_Cur}'> <input type='hidden' name='p5_Pid' value='${requestScope.p5_Pid}'> <input type='hidden' name='p6_Pcat' value='${requestScope.p6_Pcat}'> <input type='hidden' name='p7_Pdesc' value='${requestScope.p7_Pdesc}'> <input type='hidden' name='p8_Url' value='${requestScope.p8_Url}'> <input type='hidden' name='p9_SAF' value='${requestScope.p9_SAF}'> <input type='hidden' name='pa_MP' value='${requestScope.pa_MP}'> <input type='hidden' name='pd_FrpId' value='${requestScope.pd_FrpId}'> <input type="hidden" name="pr_NeedResponse" value="${requestScope.pr_NeedResponse}"> <input type='hidden' name='hmac' value='${requestScope.hmac}'> <input type='submit' /> </form> |
2.4. 支付成功回调页面
PrintWriter out = response.getWriter(); String keyValue = "w0P75wMZ203fr46r5i70V556WHFa94j14yW5J6vuh4yo3nRl5jsqF3c41677"; String r0_Cmd = formatString(request.getParameter("r0_Cmd")); String p1_MerId = "10000940764"; String r1_Code = formatString(request.getParameter("r1_Code")); String r2_TrxId = formatString(request.getParameter("r2_TrxId")); String r3_Amt = formatString(request.getParameter("r3_Amt")); String r4_Cur = formatString(request.getParameter("r4_Cur")); String r5_Pid = new String(formatString( request.getParameter("r5_Pid")).getBytes("iso-8859-1"), "UTF-8"); String r6_Order = formatString(request.getParameter("r6_Order")); String r7_Uid = formatString(request.getParameter("r7_Uid")); String r8_MP = new String(formatString( request.getParameter("r8_MP")).getBytes("iso-8859-1"), "UTF-8"); String r9_BType = formatString(request.getParameter("r9_BType")); String hmac = formatString(request.getParameter("hmac")); StringBuffer infoBuffer = new StringBuffer(); infoBuffer.append(p1_MerId); infoBuffer.append(r0_Cmd); infoBuffer.append(r1_Code); infoBuffer.append(r2_TrxId); infoBuffer.append(r3_Amt); infoBuffer.append(r4_Cur); infoBuffer.append(r5_Pid); infoBuffer.append(r6_Order); infoBuffer.append(r7_Uid); infoBuffer.append(r8_MP); infoBuffer.append(r9_BType); String md5 = DigestUtil.hmacSign(infoBuffer.toString(), keyValue); boolean isOK = md5.equals(hmac); if (isOK) { if (r1_Code.equals("1")) { //把 支付成功的订单状态改成已支付,并个给用户显示支付成功信息 //调用邮件服务接口 out.print("success !!!!"); } } else { out.println("fail !!!!"); } } |
备注:实际中,具体参数需要从数据库获取,为了安全参数提交到易宝要用post请求 ,r1_Code.equals("2")时才是真正的已付款成功,上面为1只是为了测试,返回自己的页面时,需要验证返回的hmac是否一致(不一致证明付款失败)再把订单状态修改为已支付。
下面为易宝参数说明:
正式请求地址:https://www.yeepay.com/app-merchant-proxy/node | |||||
参数名称 | 参数含义 | 是否必填 | 参数长度 | 参数说明 | 签名顺序 |
p0_Cmd | 业务类型 | 是 | Max(20) | 固定值“Buy” . | 1 |
p1_MerId | 商户编号 | 是 | Max(11) | 商户在易宝支付系统的唯一身份标识.获取方式见“如何获得商户编号” | 2 |
p2_Order | 商户订单号 | 否 | Max(50) | 若不为””,提交的订单号必须在自身账户交易中唯一;为 ”” 时,易宝支付会自动生成随机的商户订单号.易宝支付系统中对于已付或者撤销的订单,商户端不能重复提交。 | 3 |
p3_Amt | 支付金额 | 否 | Max(20) | 单位:元,精确到分.此参数为空则无法直连(如直连会报错:抱歉,交易金额太小。),必须到易宝网管让消费者输入金额 | 4 |
p4_Cur | 交易币种 | 是 | Max(10) | 固定值 ”CNY”. | 5 |
p5_Pid | 商品名称 | 否 | Max(20) | 用于支付时显示在易宝支付网关左侧的订单产品信息. 此参数如用到中文,请注意转码. | 6 |
p6_Pcat | 商品种类 | 否 | Max(20) | 商品种类. 此参数如用到中文,请注意转码. | 7 |
p7_Pdesc | 商品描述 | 否 | Max(20) | 商品描述. 此参数如用到中文,请注意转码. | 8 |
p8_Url | 商户接收支付成功数据的地址 | 否 | Max(200) | 支付成功后易宝支付会向该地址发送两次成功通知,该地址可以带参数,如: “ www.yeepay.com/callback.action?test=test”. 注意:如不填p8_Url的参数值支付成功后您将得不到支付成功的通知。 | 9 |
p9_SAF | 送货地址 | 否 | Max(1) | 为“1”: 需要用户将送货地址留在易宝支付系统;为“0”: 不需要,默认为 ”0”. | 10 |
pa_MP | 商户扩展信息 | 否 | Max(200) | 返回时原样返回,此参数如用到中文,请注意转码. | 11 |
pd_FrpId | 支付通道编码 | 否 | Max(50) | 默认为 ”” ,到易宝支付网关. 若不需显示易宝支付的页面,直接跳转到各银行、 神州行支付、骏网一卡通等支付页面, 该字段可依照附录:支付通道编码列表 设置参数值.如果此值设置错误则会报"error.noAvaliableFrp"错误 | 12 |
pr_NeedResponse | 应答机制 | 否 | Max(1) | 为“1”: 需要应答机制; 收到易宝支付服务器点对点支付成功通知,必须回写以”success”(无关大小写)开头的字符串,即使您收到成功通知时发现该订单已经处理过,也要正确回写”success”,否则易宝支付将认为您的系统没有收到通知,启动重发机制,直到收到”success”为止。 注:新商户请不要修改此参数,默认使用应答机制,无需修改。 | 13 |
hmac | 签名数据 | Max(32) | 产生hmac需要两个参数,并调用相关API. 参数1: STR,列表中的参数值按照签名顺序拼接所产生的字符串,注意null要转换为 ””,并确保无乱码. 参数2: 商户密钥.见"如何获得商户密钥" 各语言范例已经提供封装好了的方法用于生成此参数。 如果以上两个参数有错误,则该参数必然错误,见"抱歉,交易签名无效." |
关于两种通知和业务处理说明:如果用户在支付成功后,并没有通知商家而是直接关闭了重定向的窗口,那么重定向就不会通知到商户,不管用户是否重定向通知到商户,服务器点对点通知都会通知到商户,所以在callback页中r9_btype=1和r9_btype=2的两种通知类型中都要进行业务处理。并注意处理重复订单的问题,以防两次通知都处理了相同的业务造成损失。
参数名称 | 参数含义 | 参数长度 | 参数说明 | 签名顺序 |
p1_MerId | 商户编号 | Max(11) | 商户在易宝支付系统的唯一身份标识. | 1 |
r0_Cmd | 业务类型 | Max(20) | 固定值 ”Buy”. | 2 |
r1_Code | 支付结果 | 固定值 “1”, 代表支付成功. | 3 | |
r2_TrxId | 易宝支付交易流水号 | Max(50) | 易宝支付平台产生的交易流水号,每笔订单唯一 | 4 |
r3_Amt | 支付金额 | Max(20) | 单位:元,精确到分. 商户收到该返回数据后,一定用自己数据库中存储的金额与该金额进行比较. | 5 |
r4_Cur | 交易币种 | Max(10) | 返回时是"RMB" | 6 |
r5_Pid | 商品名称 | Max(20) | 易宝支付返回商户设置的商品名称. 此参数如用到中文,请注意转码. | 7 |
r6_Order | 商户订单号 | Max(50) | 易宝支付返回商户订单号. | 8 |
r7_Uid | 易宝支付会员ID | Max(50) | 如果用户使用的易宝支付会员进行支付则返回该用户的易宝支付会员ID;反之为””. | 9 |
r8_MP | 商户扩展信息 | Max(200) | 此参数如用到中文,请注意转码. | 10 |
r9_BType | 交易结果返回类型 | Max(1) | 为“1”: 浏览器重定向; 为“2”: 服务器点对点通讯. | 11 |
rb_BankId | 银行编码 | 返回用户所使用的银行编码. 该返回参数不参与到hmac校验,范例中没有收录,可根据您的需要自行添加. | ||
ro_BankOrderId | 银行订单号 | 该返回参数不参与到hmac校验,范例中没有收录,可根据您的需要自行添加. | ||
rp_PayDate | 支付成功时间 | 该返回参数不参与到hmac校验,范例中没有收录,可根据您的需要自行添加. | ||
rq_CardNo | 神州行充值卡序列号 | 若用户使用神州行卡支付,返回用户所使用的神州行卡序列号. 该返回参数不参与到hmac校验,范例中没有收录,可根据您的需要自行添加. | ||
ru_Trxtime | 交易结果通知时间 | 该返回参数不参与到hmac校验,范例中没有收录,可根据您的需要自行添加. | ||
hmac | 签名数据 | Max(32) | 产生hmac需要两个参数,并调用相关API. 参数1: STR,列表中的参数值按照签名顺序拼接所产生的字符串,注意null要转换为 ””,并确保无乱码. 参数2: 商户密钥.见"如何获得商户密钥" 各语言范例已经提供封装好了的方法用于生成此参数。 如果以上两个参数有错误,则该参数必然错误,见"抱歉,交易签名无效." |