⽀付宝⽀付设计和开发⽅案
马蓉身高背景
轮胎品牌排名在移动⽀付领域,⽀付宝⽀付占⽤巨⼤份额,根据艾瑞咨询公布的报告数据:2014Q3,⽀付宝斩获了82.6%的市场份额,在移动⽀付的霸主地位越来越稳固。财付通⽀付的发⼒点在⽀付和⼿Q⽀付,在移动⽀付格局中取得了10.0%的市场份额,排名第⼆。
⽀付宝在移动⽀付领域的统治地位,使得我们有必要梳理⽀付宝移动开发流程。本⽂写作的⽬的就是梳理⽀付流程,从架构层⾯讲述如何在移动应⽤中嵌⼊⽀付宝⽀付功能,以及指出哪些地⽅存在开发陷阱。
准备
⾸先,⽀付宝SDK下载主页的地址是:。这个地址隐藏很深,所以这⾥有必要指出。
按照说明,⾸先需要申请⽀付宝⽀付账号。这⽅⾯根据⽹站说明进⾏申请即可。⼀般需要2周左右的时间批准下来。
申请成功后账号信息包括合作者⾝份ID partner,卖家⽀付宝账号 seller_id,以及私钥 privateKey等。这三项将⽤于开发过程。
在官⽹上下载移动⽀付集成开发包。解压后, 发现其下包括三个⽂件夹(在英⽂Mac系统下⽂件名显⽰为乱码):
“商户接⼊⽀付宝收银台界⾯展⽰标准”:讲的是如何使⽤⽀付宝Logo。
“⽀付宝钱包⽀付接⼝开发包2.0标准版”:⽤于⽀付,包括客户端和服务器端开发。
“即时到账批量退款有密接⼝refund_fastpay_by_platform_pwd”:⽤于到账及批量退款,只需要服务器端操作处理。
后两个⽂件夹,都包括4⽅⾯内容:接⼝⽂档,接⼊与使⽤规则,demo代码,以及版本更新说明。
架构设计
⾸先,对于⼀个实际的App应⽤⽽⾔,可能会包括多种⽀付⽅式,因此可以采⽤设计模式中的策略Strategy模式来设计⽀付功能模块,⽀付宝⽀付作为其中的⼀个策略,pay⽅法是⽀付算法。
如果除了⽀付⽅式payment method变化,订单order也可能会有不同的形式,如格式可能不同,有些⽀持可退款,有的不允许退款等,在这种多维度可变的情况下,⽀付模块的架构可以基于桥接模式。
其次,可以把⽀付宝⽀付的各个操作步骤,⽐如获取订单号,⽣成订单数据,进⾏⽀付,获取⽀付结果,处理异常等操作,根据状态进⾏划分。这样采⽤状态模式,提供设计的灵活性和扩展性。另外也可以设计状态机进⾏统⼀的状态切换管理。下⾯为参考代码:
1 2 3
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34public class PayStateMachine {
/* all possible state of payment */
public enum PayState { PAY_INIT, PAY_GOT_CONTEXT,
PAY_UPDATED_ORDER, PAY_APPLIED_
ID, PAY_ORDER_CREATED, PAY_SUCCEED, ERROR_OCCURRED}
/* errors may occurred during payment */
public enum PayError {
PAY_GET_CONTEXT_FAIL, PAY_UPDATE_ORDER_FAIL,
PAY_APPLY_ID_FAIL, PAY_FAIL
}qq被冻结了怎么解冻
private static PayStateMachine instance;
private PayState state;
private IOrder order;
private IPayment payment;
private PayStateMachine() {
}
public static PayStateMachine getInstance() {
if (instance == null ) {
instance = new PayStateMachine();
}
return instance;
}
public void initPayment(IOrder order, IPayment payment) {
this .order = order;
最后,订单类层次可以参考模板模式来设计,例如抽象基类负责定义订单的操作框架和流程,具体订单数据的⽣成延迟到⼦类中实现。
具体实现参考附件源码。
⽀付流程
本⽂针对Android 版进⾏讲解主要的⽀付流程。IOS 版流程类似。
李祥祥个人资料从操作⾓度看⽀付流程:343536373839404142434445464748495051
5253545556
57
585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 this .order = order; this .payment = payment; this .state = PayState.PAY_INIT; } public void startPay() { changeState(PayState.PAY_INIT); } public void changeState(PayState state) { onStateChanged( this .state, state); } public void reportError(PayError error, String detail) { LogUtil.printPayLog( "the error id is:" + error + " " + detail); changeState(PayState.ERROR_OCCURRED); } private void onStateChanged(PayState oldState, PayState newState) { LogUtil.printPayLog( "oid state:" + oldState + " new state:" + newState); this .state = newState; handlePayStateChange(); } private void handlePayStateChange() { if ( this .order == null || this .payment == null ) { LogUtil.printPayLog( "Have not initiated payment" ); return ; } switch ( this .state) { case PAY_INIT: PayContext(); break ; case PAY_GOT_CONTEXT: ateO
rder(); break ; case PAY_UPDATED_ORDER: case PAY_APPLIED_ID: case PAY_ORDER_CREATED: payment.pay(order); break ; case PAY_SUCCEED: case ERROR_OCCURRED: finishProcess(); break ; default : LogUtil.printPayLog( "state is not correct!" ); finishProcess(); } } private void finishProcess() { this .order = null ; this .payment = null ; this .state = PayState.PAY_INIT; }}
操作2(调⽤⽀付接⼝)和操作7(接⼝返回⽀付结果):App与⽀付宝API的交互。
操作5(异步发送⽀付通知):⽀付宝服务器与App后台的交互。
从数据流⾓度看⽀付流程:
客户端实现
干粉灭火器使用步骤本⽂结合操作流程和数据流程,讲述主要的实现⽅案。
⾸先假设订单数据都已经存储在OrderPayModel中。
第⼀步:App客户端访问应⽤服务器,后者⽣成订单编号并返回客户端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55private void getOrderIdRequest() {
JSONObject ob = new JSONObject();
ob.put( "amount" , OrderPriceTotal());
ob.put( "productDescription" , OrderName());
ob.put( "userId" , UserId());
ob.put( "barCoupon" , OrderId());
ob.put( "barId" , BarId());
ob.put( "count" , OrderNums());
LogUtil.printPayLog( "get order id request data:"
+ String());
@Override
public void onSuccess(String content) {
super .onSuccess(content);
LogUtil.printPayLog( "get order id request is handled" );
福建铁观音PayNewOrderModel rm = new PayNewOrderModel();
rm = JSON.parseObject(content, PayNewOrderModel. class );
if (rm.getCode() != null
&& "200" .Code())) {
tradeNo = rm.getResult().getTrade_no();
LogUtil.printPayLog( "succeed to get order id:"
+ tradeNo);
orderStr = generateOrder();
PayState.PAY_APPLIED_ID);
} else {
PayError.PAY_APPLY_ID_FAIL,
"code is not right" );
}
}
@Override
public void onFailure(Throwable error, String content) {
PayError.PAY_APPLY_ID_FAIL,
第⼆步:组装订单数据,包括以下⼏个⼦步骤:创建订单数据。
对订单做RSA 签名: demo 代码中提供SingUtils 类实现该功能,即SignUtils.sign(content, RSA_PRIVATE );
对签名做 URL 编码: 调⽤java 类库接⼝,即de 来实现。565758596061626364656667686970 "failed to get order id" ); }; @Override public void onFinish() { LogUtil.LogDebug( "Payment" , "on get order id finish" , null ); }; }); }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566private String getOrderInfo(String partner, String seller) { String orderInfo; // 合作者⾝份ID orderInfo = "partner=" + "\"" + partner + "\"" ; // 卖家⽀付宝账号 orderInfo += "&seller_id=" + "\"" + seller + "\"" ; // 商户⽹站唯⼀订单号 orderInfo += "&out_tra
de_no=" + "\"" + tradeNo + "\"" ; // 商品名称 orderInfo += "&subject=" + "\"" + orderName + "\"" ; // 商品详情 orderInfo += "&body=" + "\"" + orderDetail + "\"" ; // 商品⾦额 orderInfo += "&total_fee=" + "\"" + totalPrice + "\"" ; // orderInfo += "&total_fee=" + "\"" + "0.01" + "\""; // 服务器异步通知页⾯路径 orderInfo += "¬ify_url=" + "\"" + Urls.ALI_PAY_NOTIFY + "\"" ; // 接⼝名称, 固定值 orderInfo += "&service=\"mobile.securitypay.pay\"" ; // ⽀付类型, 固定值 orderInfo += "&payment_type=\"1\"" ; // 参数编码, 固定值 orderInfo += "&_input_charset=\"utf-8\"" ; // 设置未付款交易的超时时间 // 默认30分钟,⼀旦超时,该笔交易就会⾃动被关闭。 // 取值范围:1m ~15d 。 // m-分钟,h-⼩时,d-天,1c-当天(⽆论交易何时创建,都在0点关闭)。 // 该参数数值不接受⼩数点,如1.5h ,可转换为90m 。 orderInfo += "&it_b_pay=\"30m\"" ; // ⽀付宝处理完请求后,当前页⾯跳转到商户指定页⾯的路径. // orderInfo += "&return_url=\"m.alipay\""; // Bill: this item must not be empty! though the api demo said it // can be. orderInfo += "&return_url=\"m.alipay\"" ; // 调⽤银⾏卡⽀付,需配置此参数,参与签名, 固定值 // orderInfo += "&paymethod=\"expressGateway\""; } return orderInfo; }
将订单数据和签名信息组合,⽣成符合⽀付宝参数规范的数据:
第三步:在⼦线程⾥调⽤PayTask 的pay 接⼝,将请求数据发送出去
第四步:收到⽀付处理结果的消息。⽀付 结果的状态码的意义如下 : 值为“9000”,代表⽀付成功;
值为“8000”,代表等待⽀付结果确认,这可能由于系统原因或者渠道⽀付原因。⽀付的最终结果需要由服务器端的异步通知为准(⽀付宝将向)。
值为其他,代表失败。客户端需要提⽰⽤户。
注意事项:
服务端实现 1final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();
123PayTask alipay = new PayTask(PayDemoActivity. this );// 调⽤⽀付接⼝,获取⽀付结果String result = alipay.pay(payInfo);
发布评论