支付宝当面付(条码支付)功能开发
1、什么是当面付
商户可通过以下任一方式在线下完成交易收款:
- 商家通过扫描线下买家支付宝钱包中的条码、二维码等方式将买家的交易资金直接打入卖家支付宝账户,资金实时到账;
- 线下买家通过使用支付宝钱包扫描商家的二维码等方式完成支付,提升商家收银效率,资金实时到账;
- 线下买家使用支付宝钱包中的当面付功能,通过声波支付的方式向商家完成付款,资金实时到账。
简单地说,即我们日常中使用的 条码支付、扫码支付、声波支付。
本篇的开发流程,将以条码支付为功能开发展开说明,条码支付即用户展示付款条码,商家设定金额后采用扫码枪(或输入用户条码值)扫描用户条码,直接进行扣款,所有操作由商家完成,用户仅需提供条码。
2、条码支付业务说明
使用步骤很简单:
- 用户登录支付宝钱包,在首页点击“付款”,进入付款码界面
- 收银员在商家收银系统操作生成订单
- 用户出示其“付款码”,收银员用扫码设备扫描用户手机上的条码(或手动输入条码值)后,商家收银系统提交支付
- 付款后支付宝后台会返回支付结果给商家,同时将支付情况通知到用户
3、如何接入条码支付
官方接入文档说明:当面付快速接入
3.1 创建应用和配置
3.1.1 创建应用
登陆支付宝开放平台,找到 “ 开发者中心 - 应用 - 创建应用 ”:
创建应用后,选择支付应用,因为是自己开发自己用,所以选择自用型应用,名称自定义:
3.1.2 填写资料和提交审核
功能选项中需要签约开通“当面付”,提交一些审核资料什么的,此处不展开。
这部分的说明在官方有详细文档《开放平台应用创建指南》,此处仅对 “开发配置” 作重点说明:
3.1.2.1 应用网关 和 授权回调地址
- 应用网关,用于接收支付宝异步通知(比如用户付款成功了的通知会发送到该地址,口碑开店的门店信息修改也会触发通知)
- 授权回调地址,即第三方授权或用户信息授权后回调地址
这两项是非必需填写项,根据实际情况来是否配置。
3.1.2.2 应用密钥RSA2
密钥的生成在官方有文档说明《如何生成密钥》:
- 下载Windows版RSA密钥生成器(Mac版点这里)
- 根据官方文档说明进行密钥生成,此处不再详细展开
注意:其中密钥长度为1024的即为RSA(SHA1),长度为2048的即为RSA2(SHA256)
3.1.2.3 提交审核
各项设置完成后,提交审核,审核预计在一个工作日完成,审核完成后,应用状态会显示为“已上线”。
(注意:应用命名不能出现如支付字样,于是之前我的应用因为名字“猛追湾一卡通条码支付”被驳回审核了,手动微笑)
3.2 条码支付开发
先看下官方提供的接口调用流程:
显然,如上所示,涉及三个接口:
下载好SDK之后,里面的readme也写的很明白,集成支付宝接口需要引入的文件是:
- alipay-sdk-java*.jar
- commons-logging-1.1.1.jar
在《服务端SDK》中有调用示例如下:
//实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.open.public.template.message.industry.modify
AlipayOpenPublicTemplateMessageIndustryModifyRequest request = new AlipayOpenPublicTemplateMessageIndustryModifyRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数
//此次只是参数展示,未进行字符串转义,实际情况下请转义
request.setBizContent(" {" +
" \"primary_industry_name\":\"IT科技/IT软件与服务\"," +
" \"primary_industry_code\":\"10001/20102\"," +
" \"secondary_industry_code\":\"10001/20102\"," +
" \"secondary_industry_name\":\"IT科技/IT软件与服务\"" +
" }");
AlipayOpenPublicTemplateMessageIndustryModifyResponse response = alipayClient.execute(request);
//调用成功,则处理业务逻辑
if(response.isSuccess()){
//.....
}
17
1
//实例化客户端
2
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");
3
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.open.public.template.message.industry.modify
4
AlipayOpenPublicTemplateMessageIndustryModifyRequest request = new AlipayOpenPublicTemplateMessageIndustryModifyRequest();
5
//SDK已经封装掉了公共参数,这里只需要传入业务参数
6
//此次只是参数展示,未进行字符串转义,实际情况下请转义
7
request.setBizContent(" {" +
8
" \"primary_industry_name\":\"IT科技/IT软件与服务\"," +
9
" \"primary_industry_code\":\"10001/20102\"," +
10
" \"secondary_industry_code\":\"10001/20102\"," +
11
" \"secondary_industry_name\":\"IT科技/IT软件与服务\"" +
12
" }");
13
AlipayOpenPublicTemplateMessageIndustryModifyResponse response = alipayClient.execute(request);
14
//调用成功,则处理业务逻辑
15
if(response.isSuccess()){
16
//.....
17
}
显然因为SDK封装得很友好,我们只需要传入配置参数,设置好业务内容参数,直接调起方法就可以了。下面是我个人的代码示例:
为了方便将来配置参数的修改,配置直接写到了配置文件中:
#应用id,要求应用上线,且其下功能签约状态为生效
appId=20110906...78327
#应用私钥(appId所属应用的应用私钥)RSA2加密方式
privateKey=MIIEvgI...oC6/I2xPqZnvhbntS5qi1NwYg2
#支付宝公钥
alipayPublicKey=MIIBI...wIDAQAB
8
1
#应用id,要求应用上线,且其下功能签约状态为生效
2
appId=20110906...78327
3
4
#应用私钥(appId所属应用的应用私钥)RSA2加密方式
5
privateKey=MIIEvgI...oC6/I2xPqZnvhbntS5qi1NwYg2
6
7
#支付宝公钥
8
alipayPublicKey=MIIBI...wIDAQAB
然后做一个类来进行封装开发者配置,用一个类来封装业务参数的JSON,再封装一个工具类以使用。
封装开发者配置的类:
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 支付宝API类
*/
public class AlipayAPI {
/** 应用ID */
private static String appId;
/** 应用私钥 */
private static String privateKey;
/** 支付宝公钥 */
private static String alipayPublicKey;
/** 支付调用接口 */
private static final String url_pay = "https://openapi.alipay.com/gateway.do";
static {
InputStream is = AlipayAPI.class.getResourceAsStream("/alipay.properties");
Properties prop = new Properties();
try {
prop.load(is);
setAppId(prop.getProperty("appId"));
setPrivateKey(prop.getProperty("privateKey"));
setAlipayPublicKey(prop.getProperty("alipayPublicKey"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getAppId() {
return appId;
}
public static void setAppId(String appId) {
AlipayAPI.appId = appId;
}
public static String getPrivateKey() {
return privateKey;
}
public static void setPrivateKey(String privateKey) {
AlipayAPI.privateKey = privateKey;
}
public static String getAlipayPublicKey() {
return alipayPublicKey;
}
public static void setAlipayPublicKey(String alipayPublicKey) {
AlipayAPI.alipayPublicKey = alipayPublicKey;
}
public static String getUrl_pay() {
return url_pay;
}
}
61
1
import java.io.IOException;
2
import java.io.InputStream;
3
import java.util.Properties;
4
5
/**
6
* 支付宝API类
7
*/
8
public class AlipayAPI {
9
/** 应用ID */
10
private static String appId;
11
/** 应用私钥 */
12
private static String privateKey;
13
/** 支付宝公钥 */
14
private static String alipayPublicKey;
15
16
/** 支付调用接口 */
17
private static final String url_pay = "https://openapi.alipay.com/gateway.do";
18
19
static {
20
InputStream is = AlipayAPI.class.getResourceAsStream("/alipay.properties");
21
Properties prop = new Properties();
22
try {
23
prop.load(is);
24
setAppId(prop.getProperty("appId"));
25
setPrivateKey(prop.getProperty("privateKey"));
26
setAlipayPublicKey(prop.getProperty("alipayPublicKey"));
27
28
} catch (IOException e) {
29
e.printStackTrace();
30
}
31
}
32
33
public static String getAppId() {
34
return appId;
35
}
36
37
public static void setAppId(String appId) {
38
AlipayAPI.appId = appId;
39
}
40
41
public static String getPrivateKey() {
42
return privateKey;
43
}
44
45
public static void setPrivateKey(String privateKey) {
46
AlipayAPI.privateKey = privateKey;
47
}
48
49
public static String getAlipayPublicKey() {
50
return alipayPublicKey;
51
}
52
53
public static void setAlipayPublicKey(String alipayPublicKey) {
54
AlipayAPI.alipayPublicKey = alipayPublicKey;
55
}
56
57
public static String getUrl_pay() {
58
return url_pay;
59
}
60
61
}
封装业务请求参数的类(注意,此处示例仅有部分必要参数,更多参数详见官方文档的请求参数部分):
/**
* 统一收单交易支付的参数封装类
*/
public class PayParam {
/** 商户订单 */
private String out_trade_no;
/** 支付场景 */
private String scene;
/** 支付授权码,即用户的付款码 */
private String auth_code;
/** 订单标题 */
private String subject;
/** 订单金额 */
private double total_amount;
public PayParam(String out_trade_no, String subject, double total_amount, String auth_code) {
this.out_trade_no = out_trade_no;
this.subject = subject;
this.total_amount = total_amount;
this.auth_code = auth_code;
}
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 getScene() {
return scene;
}
public void setScene(String scene) {
this.scene = scene;
}
public String getAuth_code() {
return auth_code;
}
public void setAuth_code(String auth_code) {
this.auth_code = auth_code;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public double getTotal_amount() {
return total_amount;
}
public void setTotal_amount(double total_amount) {
this.total_amount = total_amount;
}
}
62
1
/**
2
* 统一收单交易支付的参数封装类
3
*/
4
public class PayParam {
5
/** 商户订单 */
6
private String out_trade_no;
7
/** 支付场景 */
8
private String scene;
9
/** 支付授权码,即用户的付款码 */
10
private String auth_code;
11
/** 订单标题 */
12
private String subject;
13
/** 订单金额 */
14
private double total_amount;
15
16
public PayParam(String out_trade_no, String subject, double total_amount, String auth_code) {
17
this.out_trade_no = out_trade_no;
18
this.subject = subject;
19
this.total_amount = total_amount;
20
this.auth_code = auth_code;
21
}
22
23
public String getOut_trade_no() {
24
return out_trade_no;
25
}
26
27
public void setOut_trade_no(String out_trade_no) {
28
this.out_trade_no = out_trade_no;
29
}
30
31
public String getScene() {
32
return scene;
33
}
34
35
public void setScene(String scene) {
36
this.scene = scene;
37
}
38
39
public String getAuth_code() {
40
return auth_code;
41
}
42
43
public void setAuth_code(String auth_code) {
44
this.auth_code = auth_code;
45
}
46
47
public String getSubject() {
48
return subject;
49
}
50
51
public void setSubject(String subject) {
52
this.subject = subject;
53
}
54
55
public double getTotal_amount() {
56
return total_amount;
57
}
58
59
public void setTotal_amount(double total_amount) {
60
this.total_amount = total_amount;
61
}
62
}
支付工具类和测试代码:
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePayRequest;
import com.alipay.api.response.AlipayTradePayResponse;
import java.util.Date;
/**
* 支付宝的工具类
*/
public class AlipayUtil {
/** UTF8编码格式 */
private static final String CHARSET_UTF8 = "UTF-8";
/** 加密方式 */
private static final String SIGN_TYPE = "RSA2";
/** 数据格式 */
private static final String FORMAT = "JSON";
/** 支付场景,条码支付 */
private static final String SCENE_BARCODE = "bar_code";
/**
* 当面付(条码支付)
* @param content
* @return
*/
public static AlipayTradePayResponse barCodePay(PayParam content){
AlipayClient client = new DefaultAlipayClient(AlipayAPI.getUrl_pay(), AlipayAPI.getAppId(),
AlipayAPI.getPrivateKey(), FORMAT, CHARSET_UTF8, AlipayAPI.getAlipayPublicKey(), SIGN_TYPE);
AlipayTradePayRequest request = new AlipayTradePayRequest();
content.setScene(SCENE_BARCODE);
String bizContent = JSON.toJSONString(content);
request.setBizContent(bizContent);
AlipayTradePayResponse response = null;
try {
response = client.execute(request);
} catch (AlipayApiException e) {
e.printStackTrace();
}
return response;
}
/**
* 支付完成后的业务处理
* @param response
*/
public static void afterBarCodePay(AlipayTradePayResponse response) {
if (response.isSuccess()) {
//处理业务逻辑
System.out.println("start handle business");
}
}
/**
* 获取商户订单
* @return
*/
public static String getTradeNo() {
return "PAY" + new Date().getTime();
}
//测试
public static void main(String[] args) {
String tradeNo = getTradeNo();
String subject = "测试用订单";
double totalAmount = 0.01;
String authCode = "274726044141298157";
PayParam param = new PayParam(tradeNo, subject, totalAmount, authCode);
AlipayTradePayResponse response = barCodePay(param);
System.out.println(response.getBody());
afterBarCodePay(response);
}
}
x
1
import com.alibaba.fastjson.JSON;
2
import com.alipay.api.AlipayApiException;
3
import com.alipay.api.AlipayClient;
4
import com.alipay.api.DefaultAlipayClient;
5
import com.alipay.api.request.AlipayTradePayRequest;
6
import com.alipay.api.response.AlipayTradePayResponse;
7
8
import java.util.Date;
9
10
/**
11
* 支付宝的工具类
12
*/
13
public class AlipayUtil {
14
/** UTF8编码格式 */
15
private static final String CHARSET_UTF8 = "UTF-8";
16
/** 加密方式 */
17
private static final String SIGN_TYPE = "RSA2";
18
/** 数据格式 */
19
private static final String FORMAT = "JSON";
20
/** 支付场景,条码支付 */
21
private static final String SCENE_BARCODE = "bar_code";
22
23
/**
24
* 当面付(条码支付)
25
* @param content
26
* @return
27
*/
28
public static AlipayTradePayResponse barCodePay(PayParam content){
29
AlipayClient client = new DefaultAlipayClient(AlipayAPI.getUrl_pay(), AlipayAPI.getAppId(),
30
AlipayAPI.getPrivateKey(), FORMAT, CHARSET_UTF8, AlipayAPI.getAlipayPublicKey(), SIGN_TYPE);
31
32
AlipayTradePayRequest request = new AlipayTradePayRequest();
33
content.setScene(SCENE_BARCODE);
34
String bizContent = JSON.toJSONString(content);
35
request.setBizContent(bizContent);
36
37
AlipayTradePayResponse response = null;
38
try {
39
response = client.execute(request);
40
} catch (AlipayApiException e) {
41
e.printStackTrace();
42
}
43
return response;
44
}
45
46
/**
47
* 支付完成后的业务处理
48
* @param response
49
*/
50
public static void afterBarCodePay(AlipayTradePayResponse response) {
51
if (response.isSuccess()) {
52
//处理业务逻辑
53
System.out.println("start handle business");
54
}
55
}
56
57
/**
58
* 获取商户订单
59
* @return
60
*/
61
public static String getTradeNo() {
62
return "PAY" + new Date().getTime();
63
}
64
65
66
//测试
67
public static void main(String[] args) {
68
String tradeNo = getTradeNo();
69
String subject = "测试用订单";
70
double totalAmount = 0.01;
71
String authCode = "274726044141298157";
72
73
PayParam param = new PayParam(tradeNo, subject, totalAmount, authCode);
74
AlipayTradePayResponse response = barCodePay(param);
75
System.out.println(response.getBody());
76
afterBarCodePay(response);
77
}
78
79
}
因为SDK封装的缘故,所以整体过程很简单了,对于支付成功后的响应,以及其他支付查询、支付退款等功能就不再展开了。
但是自己在过程中还是有两个点觉得有点坑,再次提一下:
- 创建AlipayClient对象的传参中,公钥是支付宝公钥,而不是应用公钥
- 服务端SDK的示例中请求调用的类是AlipayOpenPublicTemplateMessageIndustryModifyResponse,实际条码支付的请求类应该调用AlipayTradePayRequest
4、参考链接
附件列表