转:http://blog.csdn.net/mozhenghua/article/details/5895250
1.业务模型分析
通过支付宝提供的Webservice接口,可以调用支付宝(https://www.alipay.com)提供的服务,这些服务在中有详细说明。
在这些服务中,服务类型大致可以分为以下几类:
- WebService查询服务:
通过服务器后台发起一个http请求,然后从服务器上返回一个Xml类型的返回结果。比如,user_query 服务,支付宝id 或者支付宝的账户email通过服务器后台查询一个支付宝会员信息。
- 带有页面跳转的交互服务:
用户在实现一个web功能的过程中需要调用到支付宝的几个web页面作为,服务流程中的一部分。比如,user_authentication服务需要调用支付宝的一个用户认证页面。Custome_sign 需要使用支付的代扣协议签订页面。
另外,在支付宝协作页面跳转回原页面的时候,需要对参数签名进行验证,或者对notifyId进行验证是否是合法的web回调请求。
2. 业务场景分析
现在支付宝提供的这些服务接口,调用端只能通过查询支付宝提供api文档,通过api文档进行接口开发。
偶在开发口碑卡项目中需要用到user_query,user_authentication,customer_sign等支付宝服务,以前,口碑的业务也有调用过支付宝的接口,于是我就想重用以前其他同学已经写过的调用支付宝服务的代码。结果发现,有很多参数已经写死了,比如key,partnerid,还有sign的算法。所以代码根本没有办法重用,只能ctrl+C加ctrl+V,而且我在开发过程中为了解决参数签名匹配不上的问题搞了很长时间。
所以,我认为应该开发一个类似淘宝TOP平台提供的API代码接口,这个api接口的开发难度比淘宝open api 应该简单,因为,在和支付宝交互的数据结构比较简单。
2.1.Api所要实现的目标
解决参数签名,调用支付宝服务过程中,参数签名的作用是保证用户请求的参数没有被黑客截取篡改参数值,在所有的请求中都会有参数签名。为了代码实现简单,先默认参数签名方式为MD5签名。参数签名应该像面向切面编程一样,在开发一个具体的结果过程中程序员不用关心参数签名,这也是做这个框架的重要意义所在。当初,我照着api文档的时候为了参数签名真是折腾了好长时间,为了避免其他同学重蹈我的覆辙。必须要将参数签名封装成一个模块能够重复调用。
2.2.支持多种字符集
这个API框架能够在多种字符集环境下使用,所以这个框架要能够支持UTF8,GBK等多种环境,只需要简单配置就能切换。
2.3.新功能容易扩充
现在实现的接口还只是支付宝提供的所有web服务中的小部分,但是已经涵盖到了所有前面提到的所有种类的服务(服务器后台请求,协同页面),如果想通过此框架实现其余的页面只需要实现少部分代码就能实现功能。
3. 实现
3.1.整体类图
![](http://hi.csdn.net/attachment/201009/19/0_1284891600bH3i.gif)
3.2.核心类
3.2.1 BasicAlipayToolkit
该类是所有支付宝服务的基类,他提供了一些所有服务接口都要调用的方法。
方法有:
- urlEncode()
将传输的值进行编码,按照GBK或者UTF8或者其他编码格式进行编码。
- sign()
将传输的参数集合进行签名
- getPartner()
取得支付宝分配给用户的partnerid
3.2.2.AlipayApply
支付宝的所有web服务都是通过http协议请求发送的,通过这个类可以通过用户设置的一些基本的参数组装出发送给支付宝的http的url,如果当请求是在页面中需要使用标签<form>来发送的话,就可以通过buildSubmitForm方法生成一个html的form表单。
这些方法有:
- prepareParamMap()
将除partner,_input_charset,service参数字段放到map的数据结构中。
- buildSubmitForm()
创建出一个from表单,可以在html页面中使用,用于在想支付宝发起请求。
- getPayGateWay()
取得支付宝请求地址,如https://www.alipay.com/cooperate/gateway.do
- buildHref()
取得向支付宝请求的url。
3.2.3 BasicWebServiceApply
- getMapResult()
解析支付宝远端的返回的XML结果,封装成一个pojo对象。
- getValueElementName()
设置支付宝返回结果中代表结果值的xml节点名称。
- createResult()
创建一个远端结果对象。
- resultCallback()
返回一个结果之后设置返回结果对象的值。
3.2.4 Profile,DefaultProfile
支付宝服务请求的用户信息封装,能够取到PartnerId和getGateway,不同的用户需要通过调用。
- Profile profile = new DefaultProfile("adfasdfasdq24234sdf3434","http://aliapi.alipay.net/gateway.do"
- ,"2088101010199999");
- BasicAlipayToolkit.setProfile(profile);
3.3. 时序图分析
支付宝提供了两种服务,webService和页面协作,根据两种服务类型,选取两个服务分析一下服务的流程。
3.3.1 user_query服务
![](http://hi.csdn.net/attachment/201009/19/0_1284891629UmBj.gif)
- 单元测试Test启动,调用BasicAlipayToolkit的静态方法setProfile()设置连接需要用到的key,gateway,和partnerid
- 调用者设置查询参数,然后调用getAlipayPojo方法,向支付宝发起查询请求。
- 支付宝返回查询结果对象AlipayQuery调用getMapResult()生成查询结果
- 封装查询结果的过程中先要生成包装查询结果的对象。
- 创建查询结果
- 根据支付宝返回的结果设置查询结果是否成功
- 如果查询有错误的话设置错误编码
- 没够成功拿到查询结果,设置查询结果对象的属性值
3.4. 功能说明
3.4.1.支付宝会员登陆验证
用户需要通过支付宝认证用户信息,代码如下:
- AlipayAuthenticateCooperate cooperate = return new AlipayAuthenticateCooperate(
- "http://localhost/callbackUrl.html",
- CharSet.GBK);
在页面上打印超链接
- <a href="<s:property escape="false" value="alipayAuthenticateCooperate.buildHref()" />">支付宝认证登录</a>
在页面上打印一个form表单
- <s:property escape="false" value="alipayAuthenticateCooperate.buildSubmitForm(‘提交’)" />
当用户在支付宝用户认证之后,跳转到原网站页面,需要取得支付宝会员信息:
- HttpServletRequest request = ServletActionContext.getRequest();
- AlipayAuthenticateCallback authenticateCallback = new AlipayAuthenticateCallback(
- CharSet.UTF8);
- AlipayResult alipayResult = authenticateCallback
- .getAlipayResult(request.getParameterMap());
- if (!alipayResult.isSuccess()) {
- throw new BizException("alipayResult is false,error code:"
- + alipayResult.getErrorCode() + "request url:["
- + request.getQueryString() + "]");}
- String alipayid = alipayResult.getUserid();
- AlipayAccount account = new AlipayAccount(alipayid, bizContext);
- account.setAccountId(alipayid + "0156");
- account.setEmail(alipayResult.getEmail());
- account.setMobile(alipayResult.getMobile());
注意:在使用该服务api代码必要代码中设置的CharSet需要和当前tomcat URLencode要一致,否则,跳转会原页面会有sign不一致的问题。
3.4.2. 即时到帐
在页面中要发起一个即时到帐的请求,代码如下:
- AlipayPayment patment = return new AlipayPayment("mozhenghua19811109@126.com",
- "http://localhost/return_url.html",
- "http://localhost/notify_url.html",
- "http://localhost/showurl.html",
- "123456", // out_trade_no
- 12f, //单价
- 3,//数量
- CharSet.GBK,
- Configuration.GetConfig("sellerEmail")// 卖家email账户
- );
- // 设置跳转的url
- action.setRedirectUrl(String.valueOf(payment.buildHref()));
用户在支付宝完成即时到帐功能之后跳转到原页面
- import com.koubei.kac.alipaytaobaocooperate.CallbackValidate;
- llbackValidate validate = new CallbackValidate();
- HttpServletRequest request = ServletActionContext.getRequest();
- // 判断url sign 是否正确
- if (!validate.isValidCallbackApply(request.getParameterMap())) {
- getLog().error(
- "is invalid callback request from alipay notfiyid:"
- + this.getNotify_id()
- + " out_trade_no[delivery_detail PK]:"
- + this.out_trade_no);
- // 跳转到错误页面
- return getErrorResult();
- }
- if (!validate.isValidNotify(this.notify_id)) {
- getLog().error(
- "not valid notifyid, notify_id:" + this.notify_id
- + " out_trade_no:" + out_trade_no
- + " trade_status:" + trade_status);
- // 非法请求跳转到错误页面
- return getErrorResult();
- }
3.4.3.CAE代扣协议
- CAEAlipayCooperate cooperate =
- return new CAEAlipayCooperate(
- "http://localhost/callback.html",
- "http://localhost/notify.html",
- "mozhenghua19811109@126.com", "CAE业务提交", CharSet.UTF8,bizCode
- );
- cooperate. buildHref();//生成cae代扣协议的url
- // 结果:
- /**
- http://aliapi.alipay.net/gateway.do?sign_type=MD5&sign=d78ce0c98deabe389c0b05dabc1ae1c9&_input_charset=utf-8&customer_email=mozhenghua19811109%40<br />
- 126.com¬ify_url=http%3A%2F%2Fhangzhou.koubei.com%2Fka%2Fnotify.html&service=customer_sign&partner=2088101010199999&type_code=123456&return_url<br />
- =http%3A%2F%2Fhangzhou.koubei.com%2Fka%2Fcallback.html
- */
- cooperate.buildSubmitForm();//生成cae代扣协议提交的form表单
- //结果:
- /**
- <form method="post" action="http://aliapi.alipay.net/gateway.do?_input_charset=utf-8"> <input type="hidden" name="_input_charset" value="utf-8" /><br />
- <input type="hidden" name="customer_email" value="mozhenghua19811109@126.com" /><br />
- <input type="hidden" name="notify_url" value="http://localhost/notify.html"/><br />
- <input type="hidden" name="service" value="customer_sign" /><br />
- <input type="hidden" name="partner" value="2088101010159999" /><br />
- <input type="hidden" name="type_code" value="123456" /><br />
- <input type="hidden" name="return_url" value="http://localhost/callback.html" /><br />
- <input type="hidden" name="sign" value="d78ce0c98deabe389c0b05dabc1ae1c9" /> <input type="hidden" name="sign_type" value="MD5" /> <input class<br />
- ="submitBtn" type="submit" value="CAE业务提交"/> </form>
- */
用户完成支付宝代扣协议跳转到口碑页面
- HttpServletRequest request = ServletActionContext.getRequest();
- CallbackValidate validate = new CallbackValidate();
- boolean isValidApply = validate.isValidCallbackApply(request
- .getParameterMap(), new CallbackValidate.KeyIgnorJudgement() {
- @Override
- public boolean ignor(String key) {
- return "sign".equalsIgnoreCase(key)
- || "sign_type".equalsIgnoreCase(key)
- || "action".equalsIgnoreCase(key);
- }
- });
- // 非法请求
- if (!isValidApply) {
- return ERROR;
- }
- // 测试该请求是否合法
- boolean isSuccess = "T".equalsIgnoreCase(request
- .getParameter("is_success"));
- if (!isSuccess) {
- return "caesingerror";
- }
如果你需要源代码,请点击下载 http://vdisk.weibo.com/s/8C6k,如果有任何疑问请发邮件,baisui@taobao.com