Bootstrap

支付宝APP支付——支付流程说明及示例

转自:https://blog.csdn.net/flygoa/article/details/54891473

 

支付宝APP支付——支付流程说明及示例
官方示例图
蚂蚁金服开放平台文档:地址

官方流程图:地址


如图,以Android平台为例:

第4步:调用支付接口:此消息就是本接口所描述的支付宝客户端SDK提供的支付对象PayTask,将商户签名后的订单信息传进payv2方法唤起支付宝收银台,交易数据格式具体参见请求参数说明。

第5步:支付请求:支付宝客户端SDK将会按照商户客户端提供的请求参数发送支付请求。

第8步:接口返回支付结果:商户客户端在第4步中调用的支付接口,会返回最终的支付结果(即同步通知),参见客户端同步返回。

第13步:用户在支付宝APP或H5收银台完成支付后,会根据商户在手机网站支付API中传入的前台回跳地址return_url自动跳转回商户页面,同时在URL请求中附带上支付结果参数。同时,支付宝还会根据原始支付API中传入的异步通知地址notify_url,通过POST请求的形式将支付结果作为参数通知到商户系统,详情见支付结果异步通知。

除了正向支付流程外,支付宝也提供交易查询、关闭、退款、退款查询以及对账等配套API。

特别注意:

    构造交易数据并签名必须在商户服务端完成,商户的应用私钥绝对不能保存在商户APP客户端中,也不能从服务端下发。

    同步返回的数据,只是一个简单的结果通知,商户确定该笔交易付款是否成功需要依赖服务端收到支付宝异步通知的结果进行判断。

    商户系统接收到通知以后,必须通过验签(验证通知中的sign参数)来确保支付通知是由支付宝发送的。建议使用支付宝提供的SDK来完成,详细验签规则参考异步通知验签。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
个人实际应用后的理解及实现
生成订单信息

app调用支付宝支付接口,服务端会返回支付信息

支付宝服务端调用异步通知接口,根据返回信息处理自己的业务逻辑,比如记入本地台账,修改订单状态等

生成订单信息
1. 创建本地订单并保存, 商品,价格,订单号


/**

 * 添加一条支付宝的订单

 * @param user

 * @param vipPrice

 * @return

 */

private VipOrder addVipOrder(User user, VipPrice vipPrice) {

    //根据业务设置订单参数

    VipOrder vo = new VipOrder();

    vo.setCostPrice(vipPrice.getCostPrice());//原价

    vo.setCurrentPrice(vipPrice.getCurrentPrice());//现价

    vo.setGoodsId(Integer.parseInt(vipPrice.getId()));//商品id

    vo.setGoodsName(vipPrice.getTypeName()+vipPrice.getName());//商品名称

    vo.setPayMode(VipOrder.PAYMODE_ALIPAY);//支付宝

    vo.setPayState(VipOrder.PAYSTATE_CREATE);//支付状态

    vo.setVipId(String.valueOf(vipPrice.getVipId()));//vipId

    vo.setVipName(vipPrice.getTypeName());//vip名称

    vo.setTimesNum(vipPrice.getTimesNum());//时长

    vo.setUserId(user.getUid());//用户id

    vo.setUserName(user.getUsername());//用户名称

    vo.setOrderNum(OrderInfoUtil2_0.getOutTradeNo(user.getUid()))//订单号,自定义随机码就可以建议32位;

    vo.setComId(String.valueOf(user.getCom_id()));//公司id

    vo.setComName(user.getCom_name());//公司名称

    vo.setIsClaim(2);//初始化账单为未索取

    vipOrderMapper.add(vo);//新增一条数据

    return vo;

}
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
订单实体


/**

 * vip_order 实体类

 * 

 * @author pzr

 */

public class VipOrder implements Serializable {

    /**

     * 支付方式 支付宝

     */

    public static final String PAYMODE_ALIPAY = "alipay";

    /**

     * 支付方式 微信

     */

    public static final String PAYMODE_WX = "wx";

    /**

     * 支付状态 创建待付款

     */

    public static final String PAYSTATE_CREATE = "create";

    /**

     * 支付状态 支付成功

     */

    public static final String PAYSTATE_SUCCESS = "success";

    /**

     * 支付状态 支付完成,完成后不可退款

     */

    public static final String PAYSTATE_FINISHED = "finished";

    /**

     * 支付状态 支付失败

     */

    public static final String PAYSTATE_FAILURE = "failure";

    private static final long serialVersionUID = -1L;

    /**

     * 套餐id

     */

    private Integer goodsId = -1;

    /**

     * 原价

     */

    private String costPrice;

    /**

     * 备注

     */

    private String remark;

    /**

     * 商品时长 单位(月)

     */

    private Integer timesNum = -1;

    /**

     * vip编号

     */

    private String orderNum;

    /**

     * 现价

     */

    private String currentPrice;

    /**

     * 

     */

    private String id;

    /**

     * 订单创建时间

     */

    private String createtime;

    /**

     * 支付状态

     */

    private String payState;

    /**

     * 处理对象,包含【处理时间】【处理类型】

     */

    private Integer handId = -1;

    /**

     * 支付方式

     */

    private String payMode;

    /**

     * 用户id

     */

    private Integer userId = -1;

    /**

     * 用户名称

     */

    private String userName;

    /**

     * 商品名称

     */

    private String goodsName;

    /**

     * 企业id

     */

    private String comId;

    /**

     * 企业名称

     */

    private String comName;

    /**

     * 会员名称

     */

    private String vipName;

    /**

     * 会员id

     */

    private String vipId;

    /**

     * 是否索取发票,【1、已索取,2、未索取,3、索取中】

     */

    private Integer isClaim = -1;

    /**

     * 是否邮寄发票,【1、是,2、否】

     */

    private Integer isMail = -1;

    public String getVipId() {

        return vipId;

    }

    public void setVipId(String vipId) {

        this.vipId = vipId;

    }

    public String getVipName() {

        return vipName;

    }

    public void setVipName(String vipName) {

        this.vipName = vipName;

    }

    public String getComId() {

        return comId;

    }

    public void setComId(String comId) {

        this.comId = comId;

    }

    public String getComName() {

        return comName;

    }

    public void setComName(String comName) {

        this.comName = comName;

    }

    public void setGoodsId(Integer goodsId) {

        this.goodsId = goodsId;

    }

    public Integer getGoodsId() {

        return goodsId;

    }

    public void setCostPrice(String costPrice) {

        this.costPrice = costPrice;

    }

    public String getCostPrice() {

        return costPrice;

    }

    public void setRemark(String remark) {

        this.remark = remark;

    }

    public String getRemark() {

        return remark;

    }

    public void setTimesNum(Integer timesNum) {

        this.timesNum = timesNum;

    }

    public Integer getTimesNum() {

        return timesNum;

    }

    public void setOrderNum(String orderNum) {

        this.orderNum = orderNum;

    }

    public String getOrderNum() {

        return orderNum;

    }

    public void setCurrentPrice(String currentPrice) {

        this.currentPrice = currentPrice;

    }

    public String getCurrentPrice() {

        return currentPrice;

    }

    public void setId(String id) {

        this.id = id;

    }

    public String getId() {

        return id;

    }

    public void setCreatetime(String createtime) {

        this.createtime = createtime;

    }

    public String getCreatetime() {

        return createtime;

    }

    public void setPayState(String payState) {

        this.payState = payState;

    }

    public String getPayState() {

        return payState;

    }

    public void setHandId(Integer handId) {

        this.handId = handId;

    }

    public Integer getHandId() {

        return handId;

    }

    public void setPayMode(String payMode) {

        this.payMode = payMode;

    }

    public String getPayMode() {

        return payMode;

    }

    public void setUserId(Integer userId) {

        this.userId = userId;

    }

    public Integer getUserId() {

        return userId;

    }

    public void setUserName(String userName) {

        this.userName = userName;

    }

    public String getUserName() {

        return userName;

    }

    public void setGoodsName(String goodsName) {

        this.goodsName = goodsName;

    }

    public String getGoodsName() {

        return goodsName;

    }

    public Integer getIsClaim() {

        return isClaim;

    }

    public void setIsClaim(Integer isClaim) {

        this.isClaim = isClaim;

    }

    public Integer getIsMail() {

        return isMail;

    }

    public void setIsMail(Integer isMail) {

        this.isMail = isMail;

    }

    // 重写Object对象的equals方法

    @Override

    public boolean equals(Object obj) {

        if (this == obj) {

            return true;

        } else {

            if (obj == null) {

                return false;

            }

            final VipOrder vipOrder = (VipOrder) obj;

            if (this.getId().equals(vipOrder.getId())) {

                return true;

            } else {

                return false;

            }

        }

    }

}
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
2. 根据订单,结合支付宝业务对象,生成订单信息字符串,并返回


/**

 * 支付宝生成订单信息

 * @param vipPrice

 * @param vo

 * @return

 */

private String aliPayHandle(VipPrice vipPrice, VipOrder vo) {

    String subject = vo.getGoodsName(); //商品标题

    String out_trade_no = vo.getOrderNum(); //商品网站唯一订单号 32位

    String total_amount = vo.getCurrentPrice(); //支付价格

    //拼装业务对象,可以设置个性化内容

    WorkParameter wp = new WorkParameter(subject, out_trade_no, total_amount);

    wp.setTimeout_express("30m");//收款时间

    wp.setBody(vipPrice.getName());

    String passback_params = vo.getId();

    try {

        passback_params = URLEncoder.encode(passback_params, "UTF-8");

    } catch (UnsupportedEncodingException e) {

        e.printStackTrace();

    }//URL编码,支付宝需要以这样的方式传递参数

    wp.setPassback_params(passback_params);//公用回传参数

    String orderInfo = OrderInfoUtil2_0.getOrderInfo(wp);//生成返回字符串

    return orderInfo;

}
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
OrderInfoUtil2_0工具类


import java.io.IOException;

import java.io.InputStream;

import java.io.UnsupportedEncodingException;

import java.net.URLEncoder;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Collections;

import java.util.Date;

import java.util.HashMap;

import java.util.List;

import java.util.Locale;

import java.util.Map;

import java.util.Properties;

import java.util.Random;

import com.fasterxml.jackson.annotation.JsonInclude.Include;

import com.fasterxml.jackson.core.JsonProcessingException;

import com.fasterxml.jackson.databind.ObjectMapper;

public class OrderInfoUtil2_0 {

    /**

     * 构造授权参数列表

     * 

     * @param pid

     * @param app_id

     * @param target_id

     * @return

     */

    public static Map<String, String> buildAuthInfoMap(String pid,

            String app_id, String target_id) {

        Map<String, String> keyValues = new HashMap<String, String>();

        // 商户签约拿到的app_id,如:2013081700024223

        keyValues.put("app_id", app_id);

        // 商户签约拿到的pid,如:2088102123816631

        keyValues.put("pid", pid);

        // 服务接口名称, 固定值

        keyValues.put("apiname", "com.alipay.account.auth");

        // 商户类型标识, 固定值

        keyValues.put("app_name", "mc");

        // 业务类型, 固定值

        keyValues.put("biz_type", "openservice");

        // 产品码, 固定值

        keyValues.put("product_id", "APP_FAST_LOGIN");

        // 授权范围, 固定值

        keyValues.put("scope", "kuaijie");

        // 商户唯一标识,如:kkkkk091125

        keyValues.put("target_id", target_id);

        // 授权类型, 固定值

        keyValues.put("auth_type", "AUTHACCOUNT");

        // 签名类型

        keyValues.put("sign_type", "RSA");

        return keyValues;

    }

    /**

     * 构造支付订单参数列表

     * 

     * @param pid

     * @param app_id

     * @param target_id

     * @return

     */

    public static Map<String, String> buildOrderParamMap(String app_id,

            WorkParameter wp) {

        InputStream in = Object.class.getResourceAsStream("/test.properties");

        Map<String, String> keyValues = new HashMap<String, String>();

        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.setSerializationInclusion(Include.NON_EMPTY);

        String biz_contentStr = "";

        try {

            biz_contentStr = objectMapper.writeValueAsString(wp);

        } catch (JsonProcessingException e) {

            e.printStackTrace();

        }

        System.out.println(biz_contentStr);

        keyValues.put("app_id", app_id);

        keyValues.put("biz_content", biz_contentStr);

        keyValues.put("charset", "utf-8");

        keyValues.put("method", "alipay.trade.app.pay");

        keyValues.put("sign_type", "RSA");

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:s");

        keyValues.put("timestamp", sdf.format(new Date()));

        keyValues.put("notify_url", AlipayConfig.notify_url);

        keyValues.put("version", "1.0");

        return keyValues;

    }

    /**

     * 构造支付订单参数信息

     * 

     * @param map

     *            支付订单参数

     * @return

     */

    public static String buildOrderParam(Map<String, String> map) {

        List<String> keys = new ArrayList<String>(map.keySet());

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < keys.size() - 1; i++) {

            String key = keys.get(i);

            String value = map.get(key);

            sb.append(buildKeyValue(key, value, true));

            sb.append("&");

        }

        String tailKey = keys.get(keys.size() - 1);

        String tailValue = map.get(tailKey);

        sb.append(buildKeyValue(tailKey, tailValue, true));

        return sb.toString();

    }

    /**

     * 拼接键值对

     * 

     * @param key

     * @param value

     * @param isEncode

     * @return

     */

    private static String buildKeyValue(String key, String value,

            boolean isEncode) {

        StringBuilder sb = new StringBuilder();

        sb.append(key);

        sb.append("=");

        if (isEncode) {

            try {

                sb.append(URLEncoder.encode(value, "UTF-8"));

            } catch (UnsupportedEncodingException e) {

                sb.append(value);

            }

        } else {

            sb.append(value);

        }

        return sb.toString();

    }

    /**

     * 对支付参数信息进行签名

     * 

     * @param map

     *            待签名授权信息

     * 

     * @return

     */

    public static String getSign(Map<String, String> map, String rsaKey) {

        List<String> keys = new ArrayList<String>(map.keySet());

        // key排序

        Collections.sort(keys);

        StringBuilder authInfo = new StringBuilder();

        for (int i = 0; i < keys.size() - 1; i++) {

            String key = keys.get(i);

            String value = map.get(key);

            authInfo.append(buildKeyValue(key, value, false));

            authInfo.append("&");

        }

        String tailKey = keys.get(keys.size() - 1);

        String tailValue = map.get(tailKey);

        authInfo.append(buildKeyValue(tailKey, tailValue, false));

        String oriSign = SignUtils.sign(authInfo.toString(), rsaKey);

        String encodedSign = "";

        try {

            encodedSign = URLEncoder.encode(oriSign, "UTF-8");

        } catch (UnsupportedEncodingException e) {

            e.printStackTrace();

        }

        return "sign=" + encodedSign;

    }

    /**

     * 要求外部订单号必须唯一。

     * 

     * @return

     */

    public static String getOutTradeNo(int id) {

        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssS",

                Locale.getDefault());

        Date date = new Date();

        String key = format.format(date);

        Random r = new Random();

        // 5位随机码

        int i = r.nextInt(100000) % (100000 - 10000 + 1) + 10000;

        // id补0

        String idStr = getIdStr(id);

        key = idStr + key + i;

        key = key.substring(0, 32);

        return key;

    }

    /**

     * id补0 字符串长度12位 id不够的补0

     * 

     * @param id

     * @return

     */

    private static String getIdStr(int id) {

        String str = "";

        if (0 < id && id < 10) {

            str = "00000000000" + id;

        }

        if (10 <= id && id < 100) {

            str = "0000000000" + id;

        }

        if (100 <= id && id < 1000) {

            str = "000000000" + id;

        }

        if (1000 <= id && id < 10000) {

            str = "00000000" + id;

        }

        if (10000 <= id && id < 100000) {

            str = "0000000" + id;

        }

        if (100000 <= id && id < 1000000) {

            str = "000000" + id;

        }

        if (1000000 <= id && id < 10000000) {

            str = "00000" + id;

        }

        return str;

    }

    /**

     * 获取订单信息

     * 

     * @param wp

     *            订单基础信息对象

     * @return

     */

    public static String getOrderInfo(WorkParameter wp) {

        Properties prop = new Properties();

        String appid = "";

        String private_key = "";

        InputStream in = OrderInfoUtil2_0.class

                .getResourceAsStream("zfbinfo.properties");

        try {

            prop.load(in);

            appid = prop.getProperty("appid").trim();

            private_key = prop.getProperty("private_key").trim();

        } catch (IOException e) {

            e.printStackTrace();

        }

        Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(appid,

                wp);

        String orderParam = OrderInfoUtil2_0.buildOrderParam(params);

        // 添加签名

        String sign = OrderInfoUtil2_0.getSign(params, private_key);

        // 组装参数

        String orderInfo = orderParam + "&" + sign;

        return orderInfo;

    }

}
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
zfbinfo.properties配置文件(支付宝参数配置文件)

可下载支付宝官方密钥生成工具

地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1


open_api_domain =   https://openapi.alipaydev.com/gateway.do

mcloud_api_domain = http://mcloudmonitor.com/gateway.do

pid = pid

appid =  appid

private_key = 私钥

public_key = 公钥

alipay_public_key = 公钥
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
业务对象


/**

 * 业务参数对象

 * @author pzr

 *

 */

public class WorkParameter {

    /**

     * 【非必填】

     * 对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。

     */

    private String body;

    /**

     * 【必填】

     * 商品的标题/交易标题/订单标题/订单关键字等。

     */

    private String subject;

    /**

     * 【必填】

     * 商户网站唯一订单号

     */

    private String out_trade_no;

    /**

     * 【非必填】

     * 该笔订单允许的最晚付款时间,逾期将关闭交易。

     * 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。

     *  该参数数值不接受小数点, 如 1.5h,可转换为 90m。

     */

    private String timeout_express;

    /**

     * 【必填】

     * 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]

     */

    private String total_amount;

    /**

     * 【非必填】

     * 收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID

     */

    private String seller_id;

    /**

     * 【必填】

     * 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY

     */

    private String product_code = "QUICK_MSECURITY_PAY";

    /**

     * 【非必填】

     * 商品主类型:0—虚拟类商品,1—实物类商品

     * 注:虚拟类商品不支持使用花呗渠道

     */

    private String goods_type;

    /**

     * 【非必填】

     * 公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。

     * 支付宝会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝

     */

    private String passback_params;

    /**

     * 【非必填】

     * 优惠参数

     * 注:仅与支付宝协商后可用

     */

    private String promo_params;

    /**

     * 【非必填】

     * 业务扩展参数,详见下面的“业务扩展参数说明”

     */

    private String extend_params;

    /**

     * 【非必填】

     * 可用渠道,用户只能在指定渠道范围内支付

     * 当有多个渠道时用“,”分隔

     * 注:与disable_pay_channels互斥

     */

    private String enable_pay_channels;

    /**

     * 【非必填】

     * 禁用渠道,用户不可用指定渠道支付

     * 当有多个渠道时用“,”分隔

     * 注:与enable_pay_channels互斥

     */

    private String disable_pay_channels;

    public WorkParameter(){

    }

    /**

     * 必填项构造方法

     * @param subject  商品的标题/交易标题/订单标题/订单关键字等。

     * @param out_trade_no 商户网站唯一订单号

     * @param total_amount 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]

     */

    public WorkParameter(String subject,String out_trade_no,String total_amount){

        this.subject = subject;

        this.out_trade_no = out_trade_no;

        this.total_amount = total_amount;

    }

    public String getBody() {

        return body;

    }

    public void setBody(String body) {

        this.body = body;

    }

    public String getSubject() {

        return subject;

    }

    public void setSubject(String subject) {

        this.subject = subject;

    }

    public String getOut_trade_no() {

        return out_trade_no;

    }

    public void setOut_trade_no(String out_trade_no) {

        this.out_trade_no = out_trade_no;

    }

    public String getTimeout_express() {

        return timeout_express;

    }

    public void setTimeout_express(String timeout_express) {

        this.timeout_express = timeout_express;

    }

    public String getTotal_amount() {

        return total_amount;

    }

    public void setTotal_amount(String total_amount) {

        this.total_amount = total_amount;

    }

    public String getSeller_id() {

        return seller_id;

    }

    public void setSeller_id(String seller_id) {

        this.seller_id = seller_id;

    }

    public String getProduct_code() {

        return product_code;

    }

    public void setProduct_code(String product_code) {

        this.product_code = product_code;

    }

    public String getGoods_type() {

        return goods_type;

    }

    public void setGoods_type(String goods_type) {

        this.goods_type = goods_type;

    }

    public String getPassback_params() {

        return passback_params;

    }

    public void setPassback_params(String passback_params) {

        this.passback_params = passback_params;

    }

    public String getPromo_params() {

        return promo_params;

    }

    public void setPromo_params(String promo_params) {

        this.promo_params = promo_params;

    }

    public String getExtend_params() {

        return extend_params;

    }

    public void setExtend_params(String extend_params) {

        this.extend_params = extend_params;

    }

    public String getEnable_pay_channels() {

        return enable_pay_channels;

    }

    public void setEnable_pay_channels(String enable_pay_channels) {

        this.enable_pay_channels = enable_pay_channels;

    }

    public String getDisable_pay_channels() {

        return disable_pay_channels;

    }

    public void setDisable_pay_channels(String disable_pay_channels) {

        this.disable_pay_channels = disable_pay_channels;

    }

}
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
app调用支付宝支付接口,服务端会返回支付信息
参考:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.DO5i9o&treeId=204&articleId=105296&docType=1

关键代码:其中orderInfo就是服务端返回的订单信息


final String orderInfo = info;   // 订单信息

Runnable payRunnable = new Runnable() {

    @Override

    public void run() {

        PayTask alipay = new PayTask(DemoActivity.this);

        String result = alipay.payV2(orderInfo,true);

        Message msg = new Message();

        msg.what = SDK_PAY_FLAG;

        msg.obj = result;

        mHandler.sendMessage(msg);

    }

};

 // 必须异步调用

Thread payThread = new Thread(payRunnable);

payThread.start();
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
支付宝服务端调用异步通知接口处理
支付宝服务端调用异步通知接口,根据返回信息处理自己的业务逻辑,比如记入本地台账,修改订单状态等

支付宝回调是要进行验签的,验证通过后在进行操作

关键代码:


/**

 * 支付宝异步回调

 * 

 * @return

 * @throws UnsupportedEncodingException

 */

@RequestMapping(value = "/vipBuy.do", method = RequestMethod.POST)

@ResponseBody

public void vipBuy(HttpServletRequest request,HttpServletResponse response)

        throws UnsupportedEncodingException {

    String str = "";

    // 获取支付宝POST过来反馈信息

    Map<String, String> params = getReqParam(request);

    // 异步通知ID

    String notify_id = request.getParameter("notify_id");

    // 交易状态

    String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

    // 获取支付宝的通知返回参数//

    if (notify_id != "" && notify_id != null) {// //判断接受的post通知中有无notify_id,如果有则是异步通知。

        boolean signVerified = false;//验签标志

        try {

            signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, "UTF-8");

        } catch (AlipayApiException e) {

            e.printStackTrace();

        }

        // 使用支付宝公钥验签

        if (signVerified){

            //获取回传参数passback_params,即是订单id

            String orderNum = vaan.getOut_trade_no();//订单号

            String orderId = vaan.getPassback_params();//订单id

            //通过订单id和订单编号获取指定的订单信息

            VipOrder vo = new VipOrder();

            vo.setId(orderId);

            vo.setOrderNum(orderNum);

            vo = vipOrderMapper.findByIdOrderNum(vo);

            //验证请求真实性

            boolean flag = checkTrue(vaan,vo);

            if(flag){

                //各状态处理

                if (trade_status.equals("TRADE_FINISHED")) {

                    //没有涉及退款流程,支付完成即是交易结束

                } else if (trade_status.equals("TRADE_SUCCESS")) {

                    //交易成功的逻辑处理

                }

                str = "success";

            }else{

                str = "failure";

            }

        } else{

            // 验证签名失败

            str = "failure";

        }

    } 

    //返回给支付宝,以免导致重复发送数据

    //程序执行完后必须打印输出“success”(不包含引号)。

    //如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。

    //一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);

    try {

        response.getWriter().print(str);

        System.out.println(str);

    } catch (IOException e) {

        e.printStackTrace();

    }

}

/**

 * 获取支付宝回调传入的参数,包含订单的所有信息

 * @param request

 * @return

 */

private Map<String, String> getReqParam(HttpServletRequest request) {

    Map<String, String> params = new HashMap<String, String>();

    Map<String, String[]> requestParams = request.getParameterMap();

    // 遍历参数

    for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {

        String name = (String) iter.next();

        String[] values = (String[]) requestParams.get(name);

        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"), "gbk");

        params.put(name, valueStr);

    }

    return params;

}

/**

 * 通过支付类型和支付返回结果,获取支付状态 1-支付成功 2-支付失败

 * @param return_code

 * @param string

 * @return

 */

private Integer getPayState(String returnCode, String payType) {

    if(payType.equals(VipOrder.PAYMODE_WX)){

        if(returnCode.equals("SUCCESS")){

            return 1;

        }else{

            return 2;

        }

    }else if(payType.equals(VipOrder.PAYMODE_ALIPAY)){

        if(returnCode.equals("TRADE_SUCCESS") || returnCode.equals("TRADE_FINISHED")){

            return 1;

        }else{

            return 2;

        }

    }

    return 2;

}

/**

 * 验证数据真实性

 * 一、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号

 * 二、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额)

 * 三、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)

 * 四、验证app_id是否为该商户本身

 * 五、验证本地订单状态是否是已经成功的,避免重复购买会员

 * 上面验证都通过,才可以进行后续工作

 * @param vaan

 * @return

 */

private boolean checkTrue(VipAlipayAsynNotify vaan,VipOrder vo) {

    //一、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号

    //如果不存在则说明反馈的订单号和订单id对不上

    if(vo == null){

        return false;

    }

    //支付完成,也不在进行业务处理

    if(vo.getPayState().equals(VipOrder.PAYSTATE_SUCCESS) || vo.getPayState().equals(VipOrder.PAYSTATE_FINISHED)){

        return false;

    }

    //二、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额)

    if(!vo.getCurrentPrice().equals(vaan.getTotal_amount())){

        return false;

    }

    //三、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)

    //四、验证app_id是否为该商户本身

    //上面四步验证都通过,才可以进行后续工作

    return true;

}

--------------------- 
作者:-贫寒豌豆 
来源:CSDN 
原文:https://blog.csdn.net/flygoa/article/details/54891473 
版权声明:本文为博主原创文章,转载请附上博文链接!

;