本文讲解了 android 开发的原生态 app 集成了支付宝支付, 还提供了 java 后台服务器处理支付宝支付的加密代码, app 前端与 java 后台服务器使用 json 数据格式交互信息,java 后台服务主要用来对支付数据进行加密和接受支付宝服务器的回调
注意: 本文即涉及到 android 前端, 也涉及到 Java 后台
准备条件:
到支付宝官网上注册用户, 打开开放平台,支付宝默认生成沙箱环境,用来测试支付流程
安装 Android Studio 【下载】 , 安装 Eclipse mars 【下载】 , JDK1.7 tomcat7
1. 支付流程概述
使用过 app 支付的用户都知道,在支付环节,如果选择【支付宝支付】, 我们的 app 会拉起支付宝 app, 并且将支付信息传入到
支付宝 app 中显示出来, 用户确认付款完成付款操作, 有朋友就会问,那我们的应用逻辑应该放在哪个步骤呢
步骤 1: 应用程序拉起支付宝 app 前,会按照支付宝的规则传递相应的数据给支付宝,所以启动支付宝前,我们需要构建数据, 基本数据包括商品描述,商品价钱等,但为了让支付宝区别是哪个应用程序传过来的数据,还需要 AppId(应用程序在支付宝平台上对应的序号); 另外为了保障数据不被篡改, 我们传递的数据需要进行 RSA 加密
步骤 2: 对数据的加密工作是放在后台进行的,那为什么不放在前台呢,熟悉 RSA 加密的朋友都知道,RSA 是非对称加密,加密方与解密方 需要一对密钥 (公钥和私钥), 这些公钥与私钥是敏感信息,放在 android 端容易被盗取,所以将公钥与私钥放在后台,后台对数据加密完成后再传给前台
步骤 3: 前台将加密完成后的数据传送给支付宝, 由支付宝完成支付
步骤 4: 支付完成后 app 端会得到相应的事件通知,前台根据相应的事件来判断前台的业务走向
步骤 5: 支付宝官方要求,使用支付宝完成支付后,支付宝后台 会调用 应用服务的后台用来告之交易支付是否真正成功
由于本地服务无法向外界提供服务, 所以支付宝回调本地测试的服务器接口无法连通,至于连通的方法,可以看一下
ngrok 的配置 , 这个工具可以穿透内网
2. Android 端
项目运行后如下图
2.1 下载导入项目到 android studio 中,将 build.gradle 中的 storeFile 的路径设置正确
2.2 android 端的通信使用 retrofit rxjava 技术,所以访问后台的请求 url 需要配置正确, 如果使用本文提供的后台,则可以不必修改,如果使用别人的后台服务,需要进行更改
3.3 android 端访问后台获取加密数据,然后将加密数据传递给支付宝
注意:由于使用的支付宝的沙箱配置信息 ,必须加上 代码 EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
如果使用正式环境的支付宝配置信息, 不能加上上面沙箱代码
3.4 支付宝支付完成后执行 handleMessage
private void aliPay(){
BizContent bizContent = new BizContent();
bizContent.setOut_trade_no(outTradeNum);
bizContent.setTotal_amount(totalPrice);
bizContent.setSubject("某个项目" + "-支付订单");
String strBizContent = new Gson().toJson(bizContent);
//generateOrderInfo去后台获取加密的信息
payService
.generateOrderInfo(strBizContent)
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
//orderInfo为加密的信息
final String orderInfo = s;
Runnable payRunnable = new Runnable() {
@Override
public void run() {
//沙箱环境开启沙箱功能
//正式环境不需要下面这行代码
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
PayTask alipay = new PayTask(MainActivity.this);
Map<String, String> result = alipay.payV2(orderInfo, true);
Log.i("msp", result.toString());
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}
};
Thread payThread = new Thread(payRunnable);
payThread.start();
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
showToast(throwable.getMessage());
}
});
}
在 handleMessage 判断支付情况, 如果 payResult.getResultStatus() =="9000" 则支付成功, 否则失败
具体的失败信息可以查看支付宝的官方文档
3. Java 后台
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@SuppressWarnings("unused")
public void handleMessage(Message msg) {
switch (msg.what) {
case SDK_PAY_FLAG: {
@SuppressWarnings("unchecked")
PayResult payResult = new PayResult((Map<String, String>) msg.obj);
/**
对于支付结果,请商户依赖服务端的异步通知结果.同步通知结果,仅作为支付结束的通知.
*/
String resultInfo = payResult.getResult();// 同步返回需要验证的信息
String resultStatus = payResult.getResultStatus();
// 判断resultStatus 为9000则代表支付成功
if (TextUtils.equals(resultStatus, "9000")) {
outTradeNum = "";
showToast("支付成功");
} else {
outTradeNum = "";
// 该笔订单真实的支付结果,需要依赖服务端的异步通知.
Toast.makeText(MainActivity.this, "支付失败", Toast.LENGTH_SHORT).show();
}
break;
}
case SDK_AUTH_FLAG: {
@SuppressWarnings("unchecked")
AuthResult authResult = new AuthResult((Map<String, String>) msg.obj, true);
String resultStatus = authResult.getResultStatus();
// 判断resultStatus 为"9000"且result_code
// 为"200"则代表授权成功,具体状态码代表含义可参考授权接口文档
if (TextUtils.equals(resultStatus, "9000") && TextUtils.equals(authResult.getResultCode(), "200")) {
// 获取alipay_open_id,调支付时作为参数extern_token 的value
// 传入,则支付账户为该授权账户
Toast.makeText(MainActivity.this,
"授权成功\n" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT)
.show();
} else {
// 其他状态值则为授权失败
Toast.makeText(MainActivity.this,
"授权失败" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT).show();
}
break;
}
default:
break;
}
};
};
3.1 修改支付宝配置信息, 实例中使用的是本人支付宝中申请的沙箱配置信息
请将 Config.properties 中的信息改成自己的
3.2 generateOrderInfo 对 android 传递的支付信息进行加密, 加密信息回传给 android 端
3.3 支付成功后,支付宝服务端回调后台应用程序接口,用来告之支付是否成功
@RequestMapping("/generateOrderInfo.htm")@ResponseBody public Result generateOrderInfo(String bizContent) {
Result result = new Result();
AlipaySignature sing = new AlipaySignature();
if (StringUtils.isEmpty(bizContent)) {
result.setData("");
result.setStateCode(Constant.RESULT_FAILURE);
result.setDesc("待加签字符串不能为空");
} else {
String appId = ConfigManager.getInstance().getConfigItem("APPID");
Map < String,
String > params = OrderInfoUtil2_0.buildOrderParamMap(appId, bizContent, true);
String orderParam = OrderInfoUtil2_0.buildOrderParam(params);
String privateKey = ConfigManager.getInstance().getConfigItem("ProjectPrivateKey");
String sign = OrderInfoUtil2_0.getSign(params, privateKey, true);
final String orderInfo = orderParam + "&" + sign;
result.setDesc("加签成功");
result.setStateCode(Constant.RESULT_SUCCESS);
result.setData(orderInfo);
}
return result;
}
关于支付宝的其它接口的更加详细信息,请参照 【服务端详解】
/**
* 支付宝的回调接口
* @param request
*/
@RequestMapping("/notifyOrderInfo.htm") public void notifyOrderInfo(HttpServletRequest request) {
if ("TRADE_SUCCESS".equals(request.getParameter("trade_status"))) {
Enumeration < ?>pNames = request.getParameterNames();
Map < String,
String > param = new HashMap < String,
String > ();
try {
while (pNames.hasMoreElements()) {
String pName = (String) pNames.nextElement();
param.put(pName, request.getParameter(pName));
}
boolean signVerified = AlipaySignature.rsaCheckV1(param, ConfigManager.getInstance().getConfigItem("AlipayPublicKey"), AlipayConstants.CHARSET_UTF8); // 校验签名是否正确
if (signVerified) {
// TODO 验签成功后
// 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
System.out.println("订单支付成功:" + JSON.toJSONString(param));
} else {
// TODO 验签失败则记录异常日志,并在response中返回failure.
}
} catch(Exception e) {
e.printStackTrace();
}
}
return;
}
DEMO 下载:http://www.wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=844
来源: https://www.cnblogs.com/ligang227/p/8304185.html