⽀付宝⽀付设计和开发⽅案
马蓉身高背景
轮胎品牌排名
在移动⽀付领域,⽀付宝⽀付占⽤巨⼤份额,根据艾瑞咨询公布的报告数据: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);