app内集成支付宝支付这块上半年就做完了,一直都比较忙也没时间去整理。马上十一了,最近项目做完空闲时间比较多,想着把之前的坑一填。
先说一下我当时遇到的坑:
一、重要配置参数出错。
这个app项目是在原有项目基础上进行的二次重构,好多东西都是沿用之前的,包括公钥、私钥这些核心配置。刚重构完以后用的都是之前的参数,代码也没有变动,但是就是无法支付。排查到最后,用支付宝提供的验签工具检测秘钥发现公钥私钥对不上。
二、官方版本变动。
第一版app是别人做的,当时支付宝支付还是旧版的写法,等我接手的时候支付宝上线了新版支付,签约支付方式都变了。要先去开放平台申请一个应用,签约相应的支付方式,配置公钥、回调这些参数等等。
对接完微信和支付宝支付,给我最强烈的感受就是支付宝的官方文档写好,简单、清晰、明了,给的demo也很容易上手,不像微信支付文档那么混乱。
先说一下思路:
1、先到支付宝开放平台创建应用获取APPID
2、配置应用的相关参数,签约申请开通对应的支付类型
3、获得支付权限后就可以着手进行代码编写:
①、组装参数分为公共请求参数和业务请求参数两部分。详见https://docs.open.alipay.com/204/105465/
②、先把业务请求参数转成json放入公共请求参数biz_content 中
③、将公共请求参数进行签名操作
④、将签名后获得的签名参数放入公共请求参数sign中
⑤、将此时的公共请求参数进行url编码
⑥、将json数据传给前端
创建应用
签约相应支付类型
配置相应参数
设置公钥
支付宝官方提供的一个签名工具
https://docs.open.alipay.com/291/105971
支付宝网关地址
沙箱
https://openapi.alipay.com/gateway.do?
真实
https://www.alipay.com/cooperate/gateway.do?
附上一组沙箱环境测试数据
http://download.csdn.net/download/tonyfreak/9996455
代码段
后台组装参数
@RequestMapping("/alipayPayRequest")
@ApiOperation(value="支付宝付款起调的接口",httpMethod="POST",response=JsonUtil.class,notes="支付宝付款起调的接口---/alipayPayRequest")
public void alipayPayRequest(@ApiParam(required=true,name="saleMoney",value="订单金额")@RequestParam(value="saleMoney") String saleMoney,
@ApiParam(required=true,name="orderNumber",value="订单编号")@RequestParam(value="orderNumber") String orderNumber,
@ApiParam(required=true,name="orderTitle",value="订单标题")@RequestParam(value="orderTitle") String orderTitle,
@ApiParam(required=true,name="description",value="订单描述")@RequestParam(value="description") String description/*,
@ApiParam(required=true,name="showUrl",value="商品展示地址")@RequestParam(value="showUrl") String showUrl*/){
try{
if(saleMoney !=null && !saleMoney.equals("") && orderNumber !=null && !orderNumber.equals("")
&& orderTitle !=null && !orderTitle.equals("") && description !=null && !description.equals("")
/*&& showUrl !=null && !showUrl.equals("")*/){
// 公共请求参数
Map<String, String> param = new HashMap<>();
param.put("app_id", AlipayConfig.appId); // 应用appId
param.put("method", "alipay.trade.app.pay"); // 交易类型
param.put("format", "json");
param.put("charset", AlipayConfig.input_charset);
param.put("timestamp", DatetimeUtil.formatDateTime(new Date()));
param.put("version", "1.0");
param.put("notify_url", AlipayConfig.notify_url); // 支付宝服务器主动通知商户服务
param.put("sign_type", AlipayConfig.sign_type);
// 支付业务请求参数
Map<String, Object> pcont = new HashMap<>();
pcont.put("out_trade_no", orderNumber); // 订单号
pcont.put("total_amount", String.valueOf(saleMoney)); // 交易金额
pcont.put("subject", orderTitle); // 订单标题
pcont.put("body", description); // 对交易或商品的描述
pcont.put("product_code", "QUICK_MSECURITY_PAY"); // 销售产品码
param.put("biz_content", JSON.toJSONString(pcont)); // 业务请求参数 不需要对json字符串转义
Map<String, String> payMap = new HashMap<>();
try {
param.put("sign", PayUtil.getSign(param, AlipayConfig.private_key)); // 业务请求参数
//payMap.put("gateWay", AlipayConfig.GateWay); // 支付宝网关
payMap.put("orderStr", PayUtil.getSignEncodeUrl(param, true));
} catch (Exception e) {
e.printStackTrace();
}
map1.put("msg", "成功");
map2.put("result", payMap);
}else{
map1.put("msg", "参数不全");
}
} catch (BusinessException e) {
e.printStackTrace();
map1.put("msg", "发生后台异常");
}
map1.put("data", map2);
map.put("result", map1);
JsonUtil.writeJson(response, JsonUtil.objectToJson(map));
}
签名工具类
/**
* 对支付参数信息进行签名
*
* @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();
boolean first = true;
for (String key : keys) {
if (first) {
first = false;
} else {
authInfo.append("&");
}
authInfo.append(key).append("=").append(map.get(key));
}
return SignUtils.sign(authInfo.toString(), rsaKey);
}
URL编码
/**
* 返回签名编码拼接url
*
* @param params
* @param isEncode
* @return
*/
public static String getSignEncodeUrl(Map<String, String> map, boolean isEncode) {
String sign = map.get("sign");
String encodedSign = "";
if (CollectionUtil.isNotEmpty(map)) {
map.remove("sign");
List<String> keys = new ArrayList<String>(map.keySet());
// key排序
Collections.sort(keys);
StringBuilder authInfo = new StringBuilder();
boolean first = true;// 是否第一个
for (String key: keys) {
if (first) {
first = false;
} else {
authInfo.append("&");
}
authInfo.append(key).append("=");
if (isEncode) {
try {
authInfo.append(URLEncoder.encode(map.get(key), AlipayConstants.CHARSET_UTF8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else {
authInfo.append(map.get(key));
}
}
try {
encodedSign = authInfo.toString() + "&sign=" + URLEncoder.encode(sign, AlipayConstants.CHARSET_UTF8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return encodedSign.replaceAll("\\+", "%20");
}
支付完成的回调
/**
* 支付宝付款成功的回调的接口
*/
@RequestMapping("/alipayPayCallBack")
@ApiOperation(value="支付宝付款成功的回调的接口",httpMethod="POST",response=JsonUtil.class,notes="支付宝支付用于回调的接口---pay/alipayPayCallBack")
public String alipayPayCallBack(){
try {
// 获取支付宝POST过来反馈信息
Map<String, String> params = new HashMap<String, String>();
Map 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"), "utf-8");
params.put(name, valueStr);
}
// 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
// 商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 交易状态
// String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
// 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
boolean check = AlipayNotify.verify(params);
if (check) {// 验证成功
System.out.println("~~~~~~~~~~~~~~~~支付宝回调付款成功~~~~~~~~~");
//
// 请在这里加上商户的业务逻辑程序代码
String orderNo = out_trade_no;// 提交订单返回的订单编号
// 支付宝交易号
String alipayOrderNo = trade_no;
OrderCZhiFu zhuOrder = orderCZhiFuService.findZhuOrderById(orderNo);
if(zhuOrder != null && !"".equals(zhuOrder)){
if("1".equals(zhuOrder.getOrderType())){// 支付状态为:已支付
response.getWriter().write(setXML("SUCCESS", ""));
}else{
/**
* 这里写回调的业务逻辑
*/
OrderCZhiFu newZhuOrder = new OrderCZhiFu();
newZhuOrder.setOrderType(OrderCZhiFu.SUCCESS_ORDER_TYPE);
newZhuOrder.setZhiFuWaterId(alipayOrderNo);// 交易号
newZhuOrder.setZhiFuType(OrderCZhiFu.ALIPAY_ZHIFU_TYPE);
newZhuOrder.setUpdateDate(DateUtil.currentTimestamp());
newZhuOrder.setId(orderNo);// 订单编号
newZhuOrder.setBusUserId(zhuOrder.getBusUserId());// C端用户id
boolean flag = orderCZhiFuService.updateZhuOrder(newZhuOrder);
if(flag){
OrderCCityGood cityGoodParame = new OrderCCityGood();
cityGoodParame.setZhiFuOrderId(orderNo);// 订单编号
OrderCCityGood cityGood = orderCCityGoodService.findCityGood(cityGoodParame);
//全网订单
if(cityGood != null && !"".equals(cityGood)){
//获取全网订单的附加信息 0--自提,1--配送
OrderCAttachInfo attachInfo = new OrderCAttachInfo();
attachInfo.setId(cityGood.getOrderCattachInfoId());
attachInfo = orderCAttachInfoService.get(attachInfo);
//获取社区店
BusCommunityPartner community = new BusCommunityPartner();
community.setId(cityGood.getCommPartId());
community = busCommunityPartnerService.findBusCommunityPartnerById(community);
//**************消息推送控制*********************
BaseSwitchControl baseSwitchControl = new BaseSwitchControl();
baseSwitchControl.setId(community.getBaseSwitchControlId());
baseSwitchControl = baseSwitchControlService.get(baseSwitchControl);
// 短信
boolean userMessageControl = baseSwitchControlService.userMessageControl(baseSwitchControl); //用户
boolean commMessageControl = baseSwitchControlService.commMessageControl(baseSwitchControl); //社区
boolean cityMessageControl = baseSwitchControlService.cityMessageControl(baseSwitchControl); //城市
// 收银
boolean goEasyControl = baseSwitchControlService.goEasyControl(baseSwitchControl);
// 极光
boolean jPushControl = baseSwitchControlService.jPushControl(baseSwitchControl);
//**************发送短信开始*************************
if(userMessageControl){
// 短信发送参数集
TreeMap<String, String> apiparamsMap = new TreeMap<String, String>();
//向指定手机号码发送模板短信,模板内可设置部分变量。使用前需要在阿里大于管理中心添加短信签名与短信模板。测试时请直接使用正式环境HTTP请求地址。
//自提或配送的推送消息
if("0".equals(attachInfo.getTiOrSend())){ //自提
// 设置短信发送模板常量
apiparamsMap.put("sms_template_code", Constant.APP_SMS_TEMPLATE_CODE_011);
}
if("1".equals(attachInfo.getTiOrSend())){ //配送
// 设置短信发送模板常量
apiparamsMap.put("sms_template_code", Constant.APP_SMS_TEMPLATE_CODE_016);
}
// 设置短信发送模板参数
String smsParam = "{'code':'"+ cityGood.getOrderNumber() +"','store':'"+ community.getCompanyName() +"'}";
apiparamsMap.put("sms_param", smsParam);
// 设置接受短信手机号码---根据用户id进行查询
BusUser userParame = new BusUser();
userParame.setId(zhuOrder.getBusUserId());
BusUser user = busUserService.findUser(userParame);
apiparamsMap.put("rec_num", user.getAccount());
// 推送短信
ServiceUtil.sendMsg(apiparamsMap);
}
//************************goEasy推送******************************************
String tiOrSend = attachInfo.getTiOrSend();
GoEasy goEasy = new GoEasy(Constant.goEasyId);
Map<String, Object> map = new HashMap<String, Object>();
//推送消息
if(goEasyControl){
if("0".equals(tiOrSend)){ // 自提
map.put("msg", "恭喜您有新的【全网自提】订单!");
map.put("status","1"); //弹提示
String jsonInfo = JSONObject.toJSONString(map);
System.out.println(jsonInfo);
goEasy.publish(community.getId(),jsonInfo);
}
if("1".equals(tiOrSend)){ // 配送
map.put("msg", "恭喜您有新的【全网配送】订单!");
map.put("status","1"); //弹提示
String jsonInfo = JSONObject.toJSONString(map);
System.out.println(jsonInfo);
goEasy.publish(community.getId(),jsonInfo);
}
}else{
map.put("status","0"); //不弹提示
String jsonInfo = JSONObject.toJSONString(map);
System.out.println(jsonInfo);
goEasy.publish(community.getId(),jsonInfo);
}
//****************************极光推送**************************************
// 执行极光推送的业务
if(jPushControl){
Map<String, String> msgMap = new HashMap<String, String>();
msgMap.put("type", "2");
msgMap.put("fromUserId", "0");
String message = null;
if("0".equals(attachInfo.getTiOrSend())){ //自提
message = "殿下!您购买的全网商品(订单号:"+ cityGood.getOrderNumber() +")已成功付款,请您于次日11点至易点鲜社区店:" + community.getCompanyName() + "店提货。祝您生活愉快!";
}
if("1".equals(attachInfo.getTiOrSend())){ //配送
message = "殿下!您购买的全网商品(订单号:"+ cityGood.getOrderNumber() +")已成功付款,次日到店后将由易点鲜社区店:" + community.getCompanyName() + "店为您送货。请您注意接听电话,祝您生活愉快!";
}
JPush.sendMessageSigleUser(zhuOrder.getBusUserId(), message, msgMap);
}
/**
* 当C端用户购买全网订单的时候会给社区合伙人和城市合伙人发送短信
*/
//**************C端用户购买全网订单发送短信给社区合伙人开始*************************
if(commMessageControl){
// 短信发送参数集
TreeMap<String, String> apiparamsMap1 = new TreeMap<String, String>();
//向指定手机号码发送模板短信,模板内可设置部分变量。使用前需要在阿里大于管理中心添加短信签名与短信模板。测试时请直接使用正式环境HTTP请求地址。
// 设置短信发送模板常量
apiparamsMap1.put("sms_template_code", Constant.APP_SMS_TEMPLATE_CODE_013);
// 设置接受短信手机号码---根据用户id进行查询
BusUser userParame = new BusUser();
userParame.setId(zhuOrder.getBusUserId());
BusUser user = busUserService.findUser(userParame);
// 设置短信发送模板参数
String smsParam1 = "{'code':'"+ user.getAccount() +"'}";
apiparamsMap1.put("sms_param", smsParam1);
// 设置接受短信手机号码---社区合伙人电话
apiparamsMap1.put("rec_num", community.getPhone());
// 推送短信
ServiceUtil.sendMsg(apiparamsMap1);
}
//**************C端用户购买全网订单发送短信给城市合伙人开始*************************
if(cityMessageControl){
BusCityPartner city = new BusCityPartner();
city.setId(cityGood.getCityPartId());//城市合伙人id
city = busCityPartnerService.findById(city);
// 短信发送参数集
TreeMap<String, String> apiparamsMap2 = new TreeMap<String, String>();
//向指定手机号码发送模板短信,模板内可设置部分变量。使用前需要在阿里大于管理中心添加短信签名与短信模板。测试时请直接使用正式环境HTTP请求地址。
// 设置短信发送模板常量
apiparamsMap2.put("sms_template_code", Constant.APP_SMS_TEMPLATE_CODE_014);
// 设置接受短信手机号码---根据用户id进行查询
BusUser userParame = new BusUser();
userParame.setId(zhuOrder.getBusUserId());
BusUser user = busUserService.findUser(userParame);
// 设置短信发送模板参数
String smsParam2 = "{'code':'"+ user.getAccount() +"','store':'"+ community.getCompanyName() +"'}";
apiparamsMap2.put("sms_param", smsParam2);
// 设置接受短信手机号码---城市合伙人电话
apiparamsMap2.put("rec_num", city.getPhone());
// 推送短信
ServiceUtil.sendMsg(apiparamsMap2);
}
}
//社区订单
OrderCCommGood comGoodParame = new OrderCCommGood();
comGoodParame.setPayId(orderNo);// 订单编号
OrderCCommGood good = orderCCommGoodService.findGood(comGoodParame);
if(good != null && !"".equals(good)){
//获取订单的附加信息 0--自提,1--配送
OrderCAttachInfo attachInfo = new OrderCAttachInfo();
attachInfo.setId(good.getOrderCattachInfoId());
attachInfo = orderCAttachInfoService.get(attachInfo);
//获取社区店
BusCommunityPartner community = new BusCommunityPartner();
community.setId(good.getCommPartId());
community = busCommunityPartnerService.findBusCommunityPartnerById(community);
//**************消息推送控制*********************
BaseSwitchControl baseSwitchControl = new BaseSwitchControl();
baseSwitchControl.setId(community.getBaseSwitchControlId());
baseSwitchControl = baseSwitchControlService.get(baseSwitchControl);
// 短信
boolean userMessageControl = baseSwitchControlService.userMessageControl(baseSwitchControl); //用户
boolean commMessageControl = baseSwitchControlService.commMessageControl(baseSwitchControl); //社区
//boolean cityMessageControl = baseSwitchControlService.cityMessageControl(baseSwitchControl); //城市
// 收银
boolean goEasyControl = baseSwitchControlService.goEasyControl(baseSwitchControl);
// 极光
boolean jPushControl = baseSwitchControlService.jPushControl(baseSwitchControl);
//**************发送短信开始*************************
if(userMessageControl){
// 短信发送参数集
TreeMap<String, String> apiparamsMap = new TreeMap<String, String>();
//向指定手机号码发送模板短信,模板内可设置部分变量。使用前需要在阿里大于管理中心添加短信签名与短信模板。测试时请直接使用正式环境HTTP请求地址。
//自提或配送的推送消息
String smsParam = null; //接收模板参数
if("0".equals(attachInfo.getTiOrSend())){ //自提
// 设置短信发送模板常量
apiparamsMap.put("sms_template_code", Constant.APP_SMS_TEMPLATE_CODE_010);
// 设置短信发送模板参数
smsParam = "{'code':'"+ good.getOrderNumber() +"'}";
}
if("1".equals(attachInfo.getTiOrSend())){ //配送
// 设置短信发送模板常量
apiparamsMap.put("sms_template_code", Constant.APP_SMS_TEMPLATE_CODE_015);
// 设置短信发送模板参数
smsParam = "{'code':'"+ cityGood.getOrderNumber() +"','store':'"+ community.getCompanyName() +"'}";
}
apiparamsMap.put("sms_param", smsParam);
// 设置接受短信手机号码---根据用户id进行查询
BusUser userParame = new BusUser();
userParame.setId(zhuOrder.getBusUserId());
BusUser user = busUserService.findUser(userParame);
apiparamsMap.put("rec_num", user.getAccount());
// 推送短信
ServiceUtil.sendMsg(apiparamsMap);
}
//************************goEasy推送******************************************
//获取社区订单的附加信息 0--自提,1--配送
String tiOrSend = attachInfo.getTiOrSend();
GoEasy goEasy = new GoEasy(Constant.goEasyId);
Map<String, Object> map = new HashMap<String, Object>();
//推送消息
if(goEasyControl){
if("0".equals(tiOrSend)){ // 自提
System.out.println("java服务器进行消息推送");
map.put("msg", "恭喜您有新的【社区店自提】订单,请及时打包!");
map.put("status","1"); //弹提示
String jsonInfo = JSONObject.toJSONString(map);
System.out.println(jsonInfo);
goEasy.publish(community.getId(),jsonInfo);
}
if("1".equals(tiOrSend)){ // 配送
System.out.println("java服务器进行消息推送");
map.put("msg", "恭喜您有新的【社区店配送】订单,请及时配送!");
map.put("status","1"); //弹提示
String jsonInfo = JSONObject.toJSONString(map);
System.out.println(jsonInfo);
goEasy.publish(community.getId(),jsonInfo);
}
}else{
map.put("status","0"); //不弹提示
String jsonInfo = JSONObject.toJSONString(map);
System.out.println(jsonInfo);
goEasy.publish(community.getId(),jsonInfo);
}
//***********************极光推送*************************
if(jPushControl){
// 执行极光推送的业务
Map<String, String> msgMap = new HashMap<String, String>();
msgMap.put("type", "2");
msgMap.put("fromUserId", "0");
//自提或配送的推送消息
String message = null;
if("0".equals(attachInfo.getTiOrSend())){ //自提
message = "殿下!您购买的社区商品(订单号:" + good.getOrderNumber() + ")可以随时在店里取货,趁着鲜嫩多汁,别让人家等太久哟~(社区店将最晚为您保留至明天下午8点,到时将自动转为已提货)";
}
if("1".equals(attachInfo.getTiOrSend())){ //配送
message = "殿下!您购买的社区商品(订单号:" + good.getOrderNumber() + ")已成功付款,稍后将由易点鲜社区店:" + community.getCompanyName() + "店为您送货。请您注意接听电话,祝您生活愉快!";
}
JPush.sendMessageSigleUser(zhuOrder.getBusUserId(), message, msgMap);
}
//**************C端用户购买社区订单发送短信给社区合伙人开始*************************
if(commMessageControl){
// 短信发送参数集
TreeMap<String, String> apiparamsMap1 = new TreeMap<String, String>();
//向指定手机号码发送模板短信,模板内可设置部分变量。使用前需要在阿里大于管理中心添加短信签名与短信模板。测试时请直接使用正式环境HTTP请求地址。
// 设置短信发送模板常量
apiparamsMap1.put("sms_template_code", Constant.APP_SMS_TEMPLATE_CODE_012);
// 设置接受短信手机号码---根据用户id进行查询
BusUser userParame = new BusUser();
userParame.setId(zhuOrder.getBusUserId());
BusUser user = busUserService.findUser(userParame);
// 设置短信发送模板参数
String smsParam1 = "{'code':'"+ user.getAccount() +"'}";
apiparamsMap1.put("sms_param", smsParam1);
// 设置接受短信手机号码---社区合伙人电话
apiparamsMap1.put("rec_num", community.getPhone());
// 推送短信
ServiceUtil.sendMsg(apiparamsMap1);
}
}
response.getWriter().write(setXML("SUCCESS", ""));
}else{
response.getWriter().write(setXML("FAIL", ""));
}
}
}else{
response.getWriter().write(setXML("FAIL", ""));
}
response.getWriter().write("SUCCESS");
} else {// 验证失败
response.getWriter().write("FAIL");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String setXML(String return_code, String return_msg) {
return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg
+ "]]></return_msg></xml>";
}