欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android支付宝支付设计开发

程序员文章站 2024-02-24 21:59:58
     在移动支付领域,支付宝支付占用巨大份额,根据艾瑞咨询公布的报告数据:2014q3,支付宝斩获了82.6%的市场份额,在移动...

     在移动支付领域,支付宝支付占用巨大份额,根据艾瑞咨询公布的报告数据:2014q3,支付宝斩获了82.6%的市场份额,在移动支付的霸主地位越来越稳固。财付通支付的发力点在微信支付和手q支付,在移动支付格局中取得了10.0%的市场份额,排名第二。

     支付宝在移动支付领域的统治地位,使得我们有必要梳理支付宝移动开发流程。本文写作的目的就是梳理支付流程,从架构层面讲述如何在移动应用中嵌入支付宝支付功能,以及指出哪些地方存在开发陷阱。

准备 
     按照说明,首先需要申请支付宝支付账号。这方面根据网站说明进行申请即可。一般需要2周左右的时间批准下来。

申请成功后账号信息包括 合作者身份id partner, 卖家支付宝账号 seller_id,以及私钥 privatekey等。这三项将用于开发过程。

     在官网上下载移动支付集成开发包。解压后, 发现其下包括三个文件夹(在英文mac系统下文件名显示为乱码):

  • “商户接入支付宝收银台界面展示标准”:讲的是如何使用支付宝logo。
  • “支付宝钱包支付接口开发包2.0标准版”:用于支付,包括客户端和服务器端开发。
  • “即时到账批量退款有密接口refund_fastpay_by_platform_pwd”:用于到账及批量退款,只需要服务器端操作处理。

后两个文件夹,都包括4方面内容:接口文档,接入与使用规则,demo代码,以及版本更新说明。
架构设计
首先,对于一个实际的app应用而言,可能会包括多种支付方式,因此可以采用设计模式中的策略strategy模式来设计支付功能模块,支付宝支付作为其中的一个策略,pay方法是支付算法。
如果除了支付方式payment method变化,订单order也可能会有不同的形式,如格式可能不同,有些支持可退款,有的不允许退款等,在这种多维度可变的情况下,支付模块的架构可以基于桥接模式。
其次,可以把支付宝支付的各个操作步骤,比如获取订单号,生成订单数据,进行支付,获取支付结果,处理异常等操作,根据状态进行划分。这样采用状态模式,提供设计的灵活性和扩展性。另外也可以设计状态机进行统一的状态切换管理。下面为参考代码:

public 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
  }
     
     
  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;
    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:
        order.getpaycontext();
        break;
      case pay_got_context:
        order.createorder();
        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;
  }
}

最后,订单类层次可以参考模板模式来设计,例如抽象基类负责定义订单的操作框架和流程,具体订单数据的生成延迟到子类中实现。

支付流程
本文针对android版进行讲解主要的支付流程,ios版流程类似。
1、客户端实现
本文结合操作流程和数据流程,讲述主要的实现方案。
首先假设订单数据都已经存储在orderpaymodel中。
第一步:app客户端访问应用服务器,后者生成订单编号并返回客户端。

private void getorderidrequest() {
 jsonobject ob = new jsonobject();
 ob.put("amount", orderpaymodel.getorderpricetotal());
        
 ob.put("productdescription", orderpaymodel.getordername());
        
        
 ob.put("userid", orderpaymodel.getuserid());
 ob.put("barcoupon", orderpaymodel.getorderid());
 ob.put("barid", orderpaymodel.getbarid());
 ob.put("count", orderpaymodel.getordernums());
 logutil.printpaylog("get order id request data:"
  + orderpaymodel.tostring());
        
        
 httprequestfactory.getinstance().dopostrequest(urls.ali_pay_apply, ob,
  new asynchttpresponsehandler() {
        
        
   @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".equalsignorecase(rm.getcode())) {
    tradeno = rm.getresult().gettrade_no();
    logutil.printpaylog("succeed to get order id:"
     + tradeno);
        
        
    orderstr = generateorder();
    paystatemachine.getinstance().changestate(
     paystate.pay_applied_id);
        
        
   } else {
    paystatemachine.getinstance().reporterror(
     payerror.pay_apply_id_fail,
     "code is not right");
   }
   }
        
        
   @override
   public void onfailure(throwable error, string content) {
   paystatemachine.getinstance().reporterror(
    payerror.pay_apply_id_fail,
    "failed to get order id");
        
        
   };
        
        
   @override
   public void onfinish() {
   logutil.logdebug("payment", "on get order id finish",
    null);
        
        
   };
  });
 }

第二步:组装订单数据,包括以下几个子步骤:
创建订单数据。

private string getorderinfo(string partner, string seller) {
 string orderinfo;
        
  // 合作者身份id
  orderinfo = "partner=" + "\"" + partner + "\"";
        
        
  // 卖家支付宝账号
  orderinfo += "&seller_id=" + "\"" + seller + "\"";
        
        
  // 商户网站唯一订单号
  orderinfo += "&out_trade_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.com\"";
  // bill: this item must not be empty! though the api demo said it
  // can be.
  orderinfo += "&return_url=\"m.alipay.com\"";
        
        
  // 调用银行卡支付,需配置此参数,参与签名, 固定值
  // orderinfo += "&paymethod=\"expressgateway\"";
 }
        
        
 return orderinfo;
 }
  • 对订单做rsa签名:  demo代码中提供singutils类实现该功能,即signutils.sign(content, rsa_private);
  • 对签名做 url编码:  调用java类库接口,即urlencoder.encode来实现。
  • 将订单数据和签名信息组合,生成符合支付宝参数规范的数据: 
final string payinfo = orderinfo + "&sign=\"" + sign + "\"&" + getsigntype();


第三步:在子线程里调用paytask的pay接口,将请求数据发送出去

paytask alipay = new paytask(paydemoactivity.this);
// 调用支付接口,获取支付结果
string result = alipay.pay(payinfo);

第四步:收到支付处理结果的消息。支付结果的状态码的意义如下:

  •     值为“9000”,代表支付成功;
  •     值为“8000”,代表等待支付结果确认,这可能由于系统原因或者渠道支付原因。支付的最终结果需要由服务器端的异步通知为准(支付宝将向)。
  •     值为其他,代表失败。客户端需要提示用户。

注意事项:
1、本文特别需要指出的是,也就是最容易出问题的就是订单数据的生成。在demo代码的 paydemoactivity类中,定义了getorderinfo方法。 其中“orderinfo += "&return_url=\"m.alipay.com\"”;”在该demo代码的注释中,虽然说是可以为空,但实际情况,如果为空,将导致支付失败。而且凭借失败状态码,难以识别具体原因。
2、支付结果,除了支付宝服务器发通知到客户端外,也会异步通知应用服务器。考虑到安全性,客户端可以根据支付宝服务器的通知,进行商业逻辑的处理,比如订单更新等,但是支付的数据入库,需要由应用服务器端根据异步通知进行操作。
2、服务端实现
服务端基本操作包括:获取支付宝账号信息(为了安全,该信息放置在服务器,而不是客户端),创建订单,支付结果异步回调,申请退款等基本操作外。另外也可能包括:更新订单(对于支持订单可修改的应用),验证消费码,查询订单记录,删除订单等操作。
本文介绍基于java平台的服务器方案。目前比较流行的框架组合是spingmvc+mybatis+mysql。
订单的创建。当用户下订单时,如果是新订单(请求的数据没有包括订单编号信息),需要创建,并返回订单号给客户端。订单类示例:

public class payorder {
       
 public string tradeno;   //随机编号
 public string amount;   //付款金额
 public string status;   //操作状态
 public string statuscode;   //操作状态代码,0-未支付,10-已支付,4000-退款中,5000-已退款,6000-付款失败,6001-取消付款,7000-已消费
 public string orderno;   //支付宝流水号
 public string productdescription;  //商品名称
 public string payno;   //消费码
 public string isrefund;   //是否申请退款
 public string createtime;   //创建时间
 public string modifytime;   //修改时间
 public string userid;   //用户id
 public integer id;    //主键
 public string pid;    //商品id
 public int buynumber;    //存储购买数量
 public int vendorid;    //商家id
}
tradeno代码订单编号。payno代码消费编号(消费码)。orderno是支付宝服务器端生成的订单号。
@requestmapping(value = "/payorder")
@responsebody
public map<string, object> pay(httpservletrequest request, httpservletresponse response) {
 map<string, object> map = jsonputil.ptomap(request);
 map<string, object> msgmap = new hashmap<string, object>();
 if (!map.isempty()) {
 try {
  log.info("执行购买前确认操作:" + map);
  string now = string.valueof(system.currenttimemillis());
  string trade_no = map.get("barid").tostring() + "-" + map.get("barcoupon").tostring() + "-" + map.get("count").tostring() + "-" + now.substring(now.length() - 6);
    
  payorder alipay = new payorder();
  alipay.setamount(map.get("amount").tostring());
  alipay.settradeno(trade_no);
  alipay.setproductdescription(map.get("productdescription").tostring());
  alipay.setcreatetime(now);
  alipay.setstatus("待支付");
  alipay.setstatuscode("0");
  alipay.setextint1(integer.parseint(map.get("count").tostring()));
  alipay.setuserid(map.get("userid").tostring());
  alipay.setpid(map.get("barcoupon").tostring());
  alipay.setextint2(integer.parseint(map.get("barid").tostring()));
    
  int flag = alipayserviceimpl.pay(alipay);
  log.info("确认操作执行结果:" + flag);
    
  map<string, object> m = new hashmap<string, object>();
  m.put("trade_no", trade_no);
  m.put("seller", new string(base64.encode(alipayconfig.seller.getbytes())));
  m.put("partner", new string(base64.encode(alipayconfig.partner.getbytes())));
  m.put("privatekey", new string(base64.encode(alipayconfig.ios_private_key.getbytes())));
    
  if (flag > 0)
  msgmap = responsemessageutil.respmsg(constance.base_success_code, "success", m);
  else
  msgmap = responsemessageutil.respmsg(constance.base_server_wrong_code, "fail");
 } catch (exception e) {
  e.printstacktrace();
  msgmap = responsemessageutil.respmsg(constance.base_server_wrong_code, "fail");
  log.error("购买前确认失败", e);
 }
 } else {
 log.info("请求参数不完整");
 msgmap = responsemessageutil.respmsg(constance.illegal_operate, "fail");
 }
 return msgmap;
}

支付宝服务器回调app服务器,通知支付结果。app服务器将相应的数据入库后,通知支付宝服务器"success" or "fail"。

@requestmapping(value = "/payover")
 @responsebody
 public string payover(httpservletrequest request, httpservletresponse response) {
 map<string, string> map = jsonputil.buildmap(request);
 string result_str = "fail";
 if (!map.isempty()) {
  if (alipayutils.checkalipay(map, false) > 0) {// 通过支付宝验证
  try {
   log.info("执行付款的回调函数传递参数:" + map);
   string now = string.valueof(system.currenttimemillis());
   string status = map.get("trade_status").tostring();
   payorder alipay = new payorder();
   alipay.settradeno(string.valueof(map.get("out_trade_no")));
   log.info("支付状态:" + status);
   if (constance.alipay_success_code.equals(status) || constance.alipay_finished_code.equals(status)) {// 支付成功
   list<alipay> ali = alipayserviceimpl.search(alipay);
   if (ali.size() == 1 && (ali.get(0).getpayno() == null || ali.get(0).getpayno().equals(""))) {// 消息未处理
    alipay pay = new alipay();
    pay.settradeno(string.valueof(map.get("out_trade_no")));
    pay.setstatus("已支付");
    pay.setstatuscode("10");
    pay.setisrefund("0");
    pay.setmodifytime(string.valueof(system.currenttimemillis()));
    pay.setpayno(new string(base64.encode(now.substring(now.length() - 10).getbytes())));
    pay.setorderno(string.valueof(map.get("trade_no")));
    int flag = alipayserviceimpl.payover(pay);
    log.info("用户付款成功" + map);
    if (flag > 0)
    result_str = "success";
   }
   } else {
   return result_str;
   }
  } catch (exception e) {
   e.printstacktrace();
   log.error("回调函数获取参数失败", e);
   return result_str;
  }
  }
 }
 return result_str;
 }

以上就是基于android支付宝支付设计和开发方案,希望对大家学习android软件编程有所帮助。