好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

java后台实现支付宝支付接口和支付宝订单查询接口(前端为APP)

最近项目app需要接入微信、 支付 宝 支付功能,在分配开发任务时,听说微信支付接口比支付宝支付接口要难实现,由于我开发经验不是那么丰富(现工作经验1年半)且未接触过支付接口开发,组里刚好又有支付接口的老司机,所以很自然把简单的支付宝接口开发任务交给了我,看来开发组的组长还是很好人的嘛.....,废话就不多说了,我们开始吧!

实现支付宝接口详细过程

1.去支付宝官网申请公司企业账号并开通一个应用,在应用里签约app支付功能

具体的申请截图步骤,在这里我就不详细说了,因为这不是文章的重点,可参考 支付宝官网 。

经过这一步,我们可以得过开发中需要用到的几个参数

①商户appid  ②商户公钥、私钥  ③支付宝公钥  ④支付宝网关地址

解释一下这几个参数:

1.商户appid是识别商户的唯一id,是让支付宝识别,我们到底是哪一个商户,这样支付宝就能识别商户对应的账号、用户号、收款账号...等等一系列信息。

2.商户公钥、私钥以及支付宝公钥这3个参数是对商户系统与支付宝进行信息交互的数字签名用的,相信各位大学里也有学过关于数字签名的一些知识,在这里,我就简单说一下我理解的过程:首先是商户系统需要给支付宝发送信息(支付、查询等等....),涉及钱方面,咱们当前要谨慎一点对吧,所以我们需要对发送之前的信息加把锁(用商户私钥进行签名),然后再发送给支付宝。支付宝收到商户发送的信息之后,发现上了把锁,那肯定得要一把钥匙(商户公钥)来解锁对吧,所以商户在跟支付宝签约app支付功能的时候,就得把这把钥匙上传给支付宝了,支付宝就可以用商户的公钥进行解锁了。反过来也是一样,支付宝需要发送信息给商户信息,先用支付宝的私钥进行签名,再发送给商户系统,商户系统收到支付宝反馈过来的信息后,再用支付宝的公钥进行解密。在这里我们并没有用到支付宝的私钥,所以我们并不需要得到支付宝的私钥。这里放一个生成私钥公钥的 支付宝官方工具

3.支付宝网关地址,是用来配置发送给支付宝的网关地址的。

2.将支付宝的sdk集成到项目系统里

支付宝的sdk指的就是支付宝提供的工具jar包给我们开发者,sdk封装了大量的基础功能,使我们可以快速开发支付宝接口。这也是我在前面说的比微信支付接口更容易实现的原因。获取支付宝sdk地址: 支付宝sdk下载地址 ,这里我选择 java 的sdk。下载解压后的sdk得到:

alipay-sdk-java20180122110032.jar、commons-logging-1.1.1.jar是我们需要导入到项目里的,因为项目后台的大致的架构是maven+springboot+jpa,所以我们需要从maven里导入jar包,首先是导入commons-logging-1.1.1.jar,不用多说,咱直接在pom.xml里加上:

?

1

2

3

4

5

<dependency>

   <groupid>commons-logging</groupid>

   <artifactid>commons-logging</artifactid>

   <version> 1.1 . 1 </version>

</dependency>

然后是alipay-sdk-java20180122110032.jar,如果你也像上面一样直接加入到pom.xml,会发现,咦,怎么一直下载不下来。当然alipy的包在线上的maven仓库并没有,所以我们需要导入到本地的maven仓库。前提是配置好maven的环境变量,将包放在g:\alipay\sdk下,然后打开dos窗口,cd进入到g:\alipay\sdk下,执行maven如下命令:

 mvn install:install-file -dgroupid=com.alipay -dartifactid=sdk-java -dversion=20180122110032 -dpackaging=jar -dfile=alipay-sdk-java20180122110032.jar

导入成功后,在项目的pom.xml里继续添加

?

1

2

3

4

5

6

<!-- 支付宝sdk -->

<dependency>

   <groupid>com.alipay</groupid>

   <artifactid>sdk-java</artifactid>

   <version> 20180122110032 </version>

</dependency>

到此,我们就顺利把支付宝sdk集成到项目里,是不是有点小兴奋,我们很快可以开发了!

3.看支付宝提供的api和网上各位牛人总结的经验,后台使用支付宝的sdk与支付宝进行交互

先看一下支付宝支付流程的图:

首先,我们来理一理开发的思路,按照我当前项目的需求,关于支付这一块大概操作流程是:用户在app上选好要购买的商品,点击[立即购买],跳转到订单详细页面。选择支付方式,点击[确定支付]跳转到支付宝app,付款完成后,跳转回app,完成支付。这个过程,当用户点击[确定支付]时,app需要调用商户后台接口。

这时候就是我们所需要做的事情:先是生成商户系统一笔未支付的订单,获得商户订单id(商户系统生成)和订单的一些其他信息,然后再调用支付宝的sdk提供的数字签名方法,将需要传给支付宝的信息进行加签,然后把加签后的字符串返回给app。app拉起支付宝app,再把这个加签的字符串传给支付宝,完成支付。app接收到同步通知后,还需要再次调用商户后台的接口(虽然同步通知也有付款情况,但需要以后台通知为准),校验订单最终的付款情况。按照支付宝api上所说,当完成支付后,支付宝会做2个操作,一个是同步返回信息给app,一个是异步通知商户后台返回支付状态等信息,并且最终的支付结果是以异步通知为准。所以我们还需要考虑到一点,就是当用户支付成功之后,商户系统暂时没有接收到支付宝的异步通知时。我们需要拿着这个商户订单id主动调用sdk支付宝的查询接口,去获取该订单的支付情况,并最终返回给app。这个查询的接口应该是给app收到同步通知后,请求商户系统后台进行校验的时候调用的。

根据我们上面思考所得,后台只需要对外提供3个接口即可

1.用户点击[立即购买]时调用商户后台接口,后台返回加签后的订单信息字符串

2.在支付完成之后,支付宝异步通知商户后台订单的付款情况

3.在支付完成之后,跳转回app时,app调用商户后台进行最终付款校验

想通想明白之后,终于接一下我们要敲代码了,哈哈哈哈

首先,我们来准备一下需要传给支付宝sdk的公共基本参数,我把参数放到一个单独的类里,你也可以放到数据库里,代码如下:

?

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

public class alipayconfig {

   // 1.商户appid

   //public static string appid = "2017..."; 

  

   //2.私钥 pkcs8格式的

   public static string rsa_private_key = "miiewaibadanbg....." ;

  

   // 3.支付宝公钥

   public static string alipay_public_key = "miibijanbgkq....." ;

  

   // 4.服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问

   public static string notify_url = "http://www.xxx.com/alipay/notify_url.do" ;

  

    //5.页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址

   public static string return_url = "http://www.xxx.com/alipay/return_url.do" ;

  

   // 6.请求支付宝的网关地址

   public static string url = "https://openapi.alipay.com/gateway.do" ; 

  

   // 7.编码

   public static string charset = "utf-8" ;

  

   // 8.返回格式

   public static string format = "json" ;

  

   // 9.加密类型

   public static string signtype = "rsa2" ;

  

}

1.实现第一个接口:用户点击[立即购买]时调用商户后台接口,后台返回加签后的订单信息字符串。我把主要的处理逻辑写在service层了,controller层直接调用就可以,这里就不放controller层的代码了

生成商户订单的代码,我就不放了,这个根据各自的业务需求来做,生成后订单之后,把订单信息传进来该方法进行处理,返回加签后的字符串,直接返回给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

55

56

57

58

59

60

61

62

/**

  * 获取支付宝加签后台的订单信息字符串

  *

  * @param request

  * @return

  */

  @override

  @transactional (propagation = propagation.required)

  public string getalipayorderstr(ordertest ordertest) {

 

  //最终返回加签之后的,app需要传给支付宝app的订单信息字符串

  string orderstring = "" ;

  logger.info( "==================支付宝下单,商户订单号为:" +ordertest.getouttradeno());

 

  //创建商户支付宝订单(因为需要记录每次支付宝支付的记录信息,单独存一个表跟商户订单表关联,以便以后查证)

  alipaymentorder alipaymentorder= new alipaymentorder();

  alipaymentorder.setcluborderid(ordertest.getid().tostring()); //商家订单主键

  alipaymentorder.setouttradeno(ordertest.getouttradeno()); //商户订单号

  alipaymentorder.settradestatus(( byte ) 0 ); //交易状态

  alipaymentorder.settotalamount( double .parsedouble(ordertest.gettotalamount())); //订单金额

  alipaymentorder.setreceiptamount( 0.00 ); //实收金额

  alipaymentorder.setinvoiceamount( 0.00 ); //开票金额

  alipaymentorder.setbuyerpayamount( 0.00 ); //付款金额

  alipaymentorder.setrefundfee( 0.00 ); //总退款金额

  

  try {

  //实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型),为了取得预付订单信息

         alipayclient alipayclient = new defaultalipayclient(alipayconfig.url, alipayconfig.appid,

          alipayconfig.rsa_private_key, alipayconfig.format, alipayconfig.charset,

          alipayconfig.alipay_public_key,alipayconfig.signtype);

    

         //实例化具体api对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay

         alipaytradeapppayrequest ali_request = new alipaytradeapppayrequest();

    

         //sdk已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式

         alipaytradeapppaymodel model = new alipaytradeapppaymodel();

    

         //业务参数传入,可以传很多,参考api

         //model.setpassbackparams(urlencoder.encode(request.getbody().tostring())); //公用参数(附加数据)

         model.setbody(ordertest.getbody());            //对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。

         model.setsubject(ordertest.getsubjecy());         //商品名称

         model.setouttradeno(ordertest.getouttradeno());      //商户订单号(自动生成)

         // model.settimeoutexpress("30m");     //交易超时时间

         model.settotalamount(ordertest.gettotalamount());     //支付金额

         model.setproductcode( "quick_msecurity_pay" );      //销售产品码(固定值)

         ali_request.setbizmodel(model);

         logger.info( "====================异步通知的地址为:" +alipayment.getnotifyurl());

         ali_request.setnotifyurl(alipayconfig.notify_url);    //异步回调地址(后台)

         ali_request.setreturnurl(alipayconfig.return_url);   //同步回调地址(app)

    

         // 这里和普通的接口调用不同,使用的是sdkexecute

  alipaytradeapppayresponse alipaytradeapppayresponse = alipayclient.sdkexecute(ali_request); //返回支付宝订单信息(预处理)

  orderstring=alipaytradeapppayresponse.getbody(); //就是orderstring 可以直接给app请求,无需再做处理。

  this .createalipaymentorder(alipaymentorder); //创建新的商户支付宝订单

 

  } catch (alipayapiexception e) {

   e.printstacktrace();

   logger.info( "与支付宝交互出错,未能生成订单,请检查代码!" );

  }

 

     return orderstring;

  }

2.实现第二个接口:在支付完成之后,支付宝异步通知商户后台订单的付款情况,这个是支付宝每隔一段时间来访问一次的接口,直到你返回success,才会停止访问,这里我分了2个地方进行调用

?

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

/**

  * 支付宝支付成功后.异步请求该接口

  * @param request

  * @return

  * @throws ioexception

  */

  @requestmapping (value= "/notify_url" ,method=requestmethod.post)

  @responsebody

  public string notify(httpservletrequest request,httpservletresponse response) throws ioexception {

  logger.info( "==================支付宝异步返回支付结果开始" );

  //1.从支付宝回调的request域中取值

  //获取支付宝返回的参数集合

     map<string, string[]> aliparams = request.getparametermap();

     //用以存放转化后的参数集合

     map<string, string> conversionparams = new hashmap<string, string>();

    for (iterator<string> iter = aliparams.keyset().iterator(); iter.hasnext();) {

      string key = iter.next();

      string[] values = aliparams.get(key);

      string valuestr = "" ;

      for ( int i = 0 ; i < values.length; i++) {

        valuestr = (i == values.length - 1 ) ? valuestr + values[i] : valuestr + values[i] + "," ;

      }

      // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化

      // valuestr = new string(valuestr.getbytes("iso-8859-1"), "uft-8");

      conversionparams.put(key, valuestr);

    } 

    logger.info( "==================返回参数集合:" +conversionparams);

   string status=alipaymentorderservice.notify(conversionparams);

   return status;

  }

?

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

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

/**

* 支付宝异步请求逻辑处理

* @param request

* @return

* @throws ioexception

*/

public string notify(map<string, string> conversionparams){

 

logger.info( "==================支付宝异步请求逻辑处理" );

 

  //签名验证(对支付宝返回的数据验证,确定是支付宝返回的)

   boolean signverified = false ; 

  

   try {

     //调用sdk验证签名

     signverified = alipaysignature.rsacheckv1(conversionparams, alipayconfig.alipay_public_key, alipayconfig.charset, alipayconfig.signtype);

    

   } catch (alipayapiexception e) {

   logger.info( "==================验签失败 !" );

     e.printstacktrace();    

   } 

  

   //对验签进行处理

   if (signverified) {

   //验签通过   

   //获取需要保存的数据

     string appid=conversionparams.get( "app_id" ); //支付宝分配给开发者的应用id

   string notifytime=conversionparams.get( "notify_time" ); //通知时间:yyyy-mm-dd hh:mm:ss

   string gmtcreate=conversionparams.get( "gmt_create" ); //交易创建时间:yyyy-mm-dd hh:mm:ss

   string gmtpayment=conversionparams.get( "gmt_payment" ); //交易付款时间

   string gmtrefund=conversionparams.get( "gmt_refund" ); //交易退款时间

   string gmtclose=conversionparams.get( "gmt_close" ); //交易结束时间

   string tradeno=conversionparams.get( "trade_no" ); //支付宝的交易号

   string outtradeno = conversionparams.get( "out_trade_no" ); //获取商户之前传给支付宝的订单号(商户系统的唯一订单号)

   string outbizno=conversionparams.get( "out_biz_no" ); //商户业务号(商户业务id,主要是退款通知中返回退款申请的流水号)

   string buyerlogonid=conversionparams.get( "buyer_logon_id" ); //买家支付宝账号

   string sellerid=conversionparams.get( "seller_id" ); //卖家支付宝用户号

   string selleremail=conversionparams.get( "seller_email" ); //卖家支付宝账号

   string totalamount=conversionparams.get( "total_amount" ); //订单金额:本次交易支付的订单金额,单位为人民币(元)

   string receiptamount=conversionparams.get( "receipt_amount" ); //实收金额:商家在交易中实际收到的款项,单位为元

   string invoiceamount=conversionparams.get( "invoice_amount" ); //开票金额:用户在交易中支付的可开发票的金额

   string buyerpayamount=conversionparams.get( "buyer_pay_amount" ); //付款金额:用户在交易中支付的金额 

   string tradestatus = conversionparams.get( "trade_status" ); // 获取交易状态

  

   //支付宝官方建议校验的值(out_trade_no、total_amount、sellerid、app_id)

   alipaymentorder alipaymentorder= this .selectbyouttradeno(outtradeno);

 

   if (alipaymentorder!= null &&totalamount.equals(alipaymentorder.gettotalamount().tostring())&&alipayconfig.appid.equals(appid)){

    //修改数据库支付宝订单表(因为要保存每次支付宝返回的信息到数据库里,以便以后查证)

    alipaymentorder.setnotifytime(dateformat(notifytime));

    alipaymentorder.setgmtcreate(dateformat(gmtcreate));

    alipaymentorder.setgmtpayment(dateformat(gmtpayment));

    alipaymentorder.setgmtrefund(dateformat(gmtrefund));

    alipaymentorder.setgmtclose(dateformat(gmtclose));

    alipaymentorder.settradeno(tradeno);

    alipaymentorder.setoutbizno(outbizno);

    alipaymentorder.setbuyerlogonid(buyerlogonid);

    alipaymentorder.setsellerid(sellerid);

    alipaymentorder.setselleremail(selleremail);

    alipaymentorder.settotalamount( double .parsedouble(totalamount));

    alipaymentorder.setreceiptamount( double .parsedouble(receiptamount));

    alipaymentorder.setinvoiceamount( double .parsedouble(invoiceamount));

    alipaymentorder.setbuyerpayamount( double .parsedouble(buyerpayamount));

    switch (tradestatus) // 判断交易结果

           {

           case "trade_finished" : // 交易结束并不可退款

           alipaymentorder.settradestatus(( byte ) 3 );

             break ;

           case "trade_success" : // 交易支付成功

           alipaymentorder.settradestatus(( byte ) 2 );       

             break ;

           case "trade_closed" : // 未付款交易超时关闭或支付完成后全额退款

           alipaymentorder.settradestatus(( byte ) 1 );

             break ;

           case "wait_buyer_pay" : // 交易创建并等待买家付款

           alipaymentorder.settradestatus(( byte ) 0 );

             break ;

           default :

             break ;

           }

    int returnresult= this .updatebyprimarykey(alipaymentorder);  //更新交易表中状态

         

     if (tradestatus.equals( "trade_success" )) {  //只处理支付成功的订单: 修改交易表状态,支付成功

     

       if (returnresult> 0 ){

          return "success" ;

       } else {

          return "fail" ;

       }

     } else {

       return "fail" ;

     }

   

   } else {

    logger.info( "==================支付宝官方建议校验的值(out_trade_no、total_amount、sellerid、app_id),不一致!返回fail" );

    return "fail" ;

   }

 

   } else { //验签不通过 

   logger.info( "==================验签不通过 !" );

     return "fail" ;

   }

 

}

3.实现第三个接口:在支付完成之后,跳转回app时,app调用商户后台进行最终付款校验。我把主要的处理逻辑写在service层了,controller层直接调用就可以,这里就不放controller层的代码了。

?

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

55

56

57

58

/**

  * 向支付宝发起订单查询请求

  * @param request

  * @return

  * @throws ioexception

  */

  @override

  public byte checkalipay(string outtradeno) {

  logger.info( "==================向支付宝发起查询,查询商户订单号为:" +outtradeno);

 

  try {

  //实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型)

         alipayclient alipayclient = new defaultalipayclient(alipayconfig.url, alipayconfig.appid,

      alipayconfig.rsa_private_key, alipayconfig.format, alipayconfig.charset,

      alipayconfig.alipay_public_key,alipayconfig.signtype);

  alipaytradequeryrequest alipaytradequeryrequest = new alipaytradequeryrequest();

  alipaytradequeryrequest.setbizcontent( "{" +

  "\"out_trade_no\":\"" +outtradeno+ "\"" +

  "}" );

  alipaytradequeryresponse alipaytradequeryresponse = alipayclient.execute(alipaytradequeryrequest);

  if (alipaytradequeryresponse.issuccess()){

  

    alipaymentorder alipaymentorder= this .selectbyouttradeno(outtradeno);

    //修改数据库支付宝订单表

    alipaymentorder.settradeno(alipaytradequeryresponse.gettradeno());

    alipaymentorder.setbuyerlogonid(alipaytradequeryresponse.getbuyerlogonid());

    alipaymentorder.settotalamount( double .parsedouble(alipaytradequeryresponse.gettotalamount()));

    alipaymentorder.setreceiptamount( double .parsedouble(alipaytradequeryresponse.getreceiptamount()));

    alipaymentorder.setinvoiceamount( double .parsedouble(alipaytradequeryresponse.getinvoiceamount()));

    alipaymentorder.setbuyerpayamount( double .parsedouble(alipaytradequeryresponse.getbuyerpayamount()));

    switch (alipaytradequeryresponse.gettradestatus()) // 判断交易结果

           {

           case "trade_finished" : // 交易结束并不可退款

            alipaymentorder.settradestatus(( byte ) 3 );

             break ;

           case "trade_success" : // 交易支付成功

            alipaymentorder.settradestatus(( byte ) 2 );     

             break ;

           case "trade_closed" : // 未付款交易超时关闭或支付完成后全额退款

            alipaymentorder.settradestatus(( byte ) 1 );     

             break ;

           case "wait_buyer_pay" : // 交易创建并等待买家付款

            alipaymentorder.settradestatus(( byte ) 0 );

             break ;

           default :

             break ;

           }

    this .updatebyprimarykey(alipaymentorder); //更新表记录

    return alipaymentorder.gettradestatus();

  } else {

   logger.info( "==================调用支付宝查询接口失败!" );

  }

  } catch (alipayapiexception e) {

   // todo auto-generated catch block

   e.printstacktrace();

  }

  return 0 ;

  }

至此,代码已经上完了,里面可能涉及部分业务代码,如果各位需要拿代码,需要把业务代码换成自己所需要的。

建议:可以边看api边进行开发,主要是看我们需要给支付传什么参数,支付宝可以给我们传什么参数,不然没看清除,你会多很多坑要踩的,亲试过。

感觉太少图片了,这里给几张api的图片。。你们也可以自己去看 一些 支付宝API

4.关于测试的一些事

支付宝有提供沙箱环境进行测试所使用,见 支付宝沙箱调试指南 。

但是楼主比较有米,直接用真实环境进行测试,其实测一次0.01元也是挺贵的吧,前提是你的电脑必须访问外网和能够被外网所访问,建议可以找个内网穿透的工具。

写了一整个下午,好累啊,本人新手,如有错误,请各位大神指教,希望对大家有用!!!!也希望大家多多支持。

原文链接:https://blog.csdn.net/Ouyzc/article/details/79551714

查看更多关于java后台实现支付宝支付接口和支付宝订单查询接口(前端为APP)的详细内容...

  阅读:40次