由于最近用到了支付宝支付和微信支付,所以记录一下以备自己以后用到使用,本篇文章介绍一下Java实现支付宝支付Web端和Android端对应后台代码,话不多说,进入正题
本文以支付宝沙箱为主来做介绍,生产环境跟沙箱基本一致,能做到生产环境的一半也都能看出来哪里不一致,这里不做介绍
1.首先进入到支付宝开放平台https://open.alipay.com/platform/home.htm,用支付宝登录,然后点开发中心,选择沙箱,如下
2.在这个页面我们可以看到支付宝几个重要的参数,支付宝应用APPID,支付宝网关,支付宝秘钥(应用公钥,应用私钥,支付宝公钥)
APPID和支付宝网关可以直接看到,新用户进来之后RSA2秘钥是空的,这个时候我们可以这样去设置拿到应用的公钥和私钥以及支付宝公钥,如下图,点击之后会进入到获取应用的公钥和私钥的流程,这里不多介绍,填写完这几个之后,咱们来编写一下后台的应用
3.创建工程什么的这里就不做太多介绍了,我这里的工程时SpringBoot工程,目录结构如下:
4.首先来看下alipay.properties配置文件中配置的信息,只简单的说一下ALIPAY.NOTIFY_URL和ALIPAY.RUTURNA_URL这两个参数,notify_url是支付宝支付完成之后,支付宝回调函数,returna_url是web端或者Android端支付成功后回调函数,个人感觉returna_url可以不用写,这两个函数会在后面进行介绍
#需要配置,支付宝私钥(生产环境和沙箱环境生成方式稍微有点差别)
ALIPAY.PRIVATEKEY=这个地方是自己生成的应用私钥
#需要配置,支付宝公钥(生产环境和沙箱环境生成方式稍微有点差别)
ALIPAY.PUBLICKEY=应用公钥
#支付宝解签公钥(这个需要注意一下)
ALIPAY.RETURNPUBLICKEY=支付宝公钥,用于解签
#支付宝应用APPID
ALIPAY.APPID=2016092300574560
#支付宝网关
#ALIPAY.SERVER=https://openapi.alipay.com/gateway.do
ALIPAY.SERVER=https://openapi.alipaydev.com/gateway.do
#公网可以访问的地址
ALIPAY.NOTIFY_URL=http://localhost:8080/pay/notify
#公网可以访问的地址
ALIPAY.RETURNA_URL=http://localhost:8080/pay/notify
ALIPAY.SIGN=RSA2
5.完成配置文件之后,进入到主要的流程当中,创建Java类来实现支付宝网页支付(由于沙箱不支持Android测试,所以以网页版为例介绍,文中会包含Android支付代码)
5.1》首先需要引入支付宝SDK和gson的jar包,在pom.xml文件中加入两个包即可,如下:
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
5.2》jar包引入之后,实现配置文件的读取,AliPayConfig.java类,类中省略了get/set方法,代码如下:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* Created by zj on 2018/12/20.
*/
@Configuration
@PropertySource(value = {"classpath:alipay.properties"})
@ConfigurationProperties
public class AliPayConfig {
@Value("${ALIPAY.APPID}")
private String alipay_app_id;
@Value("${ALIPAY.PRIVATEKEY}")
private String merchant_private_key;
@Value("${ALIPAY.PUBLICKEY}")
private String alipay_public_key;
@Value("${ALIPAY.RETURNPUBLICKEY}")
private String alipay_return_public_key;
@Value("${ALIPAY.NOTIFY_URL}")
private String alipay_notify_url;
@Value("${ALIPAY.RETURNA_URL}")
private String return_url;
@Value("${ALIPAY.SIGN}")
private String sign_type;
// private String charset = "utf-8";
@Value("${ALIPAY.SERVER}")
private String gatewayUrL;
这里省略了几个参数的get/set方法
}
配置文件做过之后在写下支付bean类代码AlipayVo.java(此处也省略set/get方法),代码如下:
import java.io.Serializable;
/**
* Created by zj on 2018/11/30.
*/
public class AlipayVo implements Serializable {
// 订单名称
private String subject;
//商户网站唯一订单号
private String out_trade_no;
//该笔订单允许的最晚付款时间
private String timeout_express;
//付款金额
private String total_amount;
//销售产品码,与支付宝签约的产品码名称
private String product_code;
//订单描述
private String body;
省略get/set方法
}
5.3》接下来就是重要的代码了,首先来看下web端支付的代码,访问路径本文为例就是localhost:8080/pay/alipayWeb,会返回一个支付宝对应的收钱页面
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.google.gson.Gson;
import com.zj.alipay.bean.AlipayVo;
import com.zj.alipay.config.AliPayConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Created by zj on 2019/1/4.
*/
@Controller()
@RequestMapping("pay")
public class AlipayController {
@Autowired
private AliPayConfig aliPayConfig;
@RequestMapping("/alipayWeb")
@ResponseBody
private String alipaytest(String orderid,String totalAmount,String subject,String body) throws AlipayApiException {
AlipayVo vo = new AlipayVo();
//订单号
vo.setOut_trade_no(orderid);
//支付金额
vo.setTotal_amount(totalAmount);
//商品名称
vo.setSubject(subject);
//商品描述
vo.setBody(body);
vo.setProduct_code("FAST_INSTANT_TRADE_PAY"); //这个是固定的
String json = new Gson().toJson(vo);
AlipayClient alipayClient = new DefaultAlipayClient(aliPayConfig.getGatewayUrL()
, aliPayConfig.getAlipay_app_id(), aliPayConfig.getMerchant_private_key()
, "json","UTF-8",aliPayConfig.getAlipay_public_key()
, aliPayConfig.getSign_type());
// 设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(aliPayConfig.getReturn_url());
//设置支付宝可访问外网URL
alipayRequest.setNotifyUrl(aliPayConfig.getAlipay_notify_url());
alipayRequest.setBizContent(json);
String result = alipayClient.pageExecute(alipayRequest).getBody();
return result; //这里生成一个表单,会自动提交
}
}
如果使用的沙箱,咱们可以下载一个支付宝的测试环境,扫码支付,那么来了,支付完之后跳转到哪里呢,就是咱们之前在配置文件中配置的两个url了,这两个方法代码一样,里面需要注意的是支付宝公钥(我做的时候踩过这里的坑),回调函数中有解签函数,用到了一个公钥,当时在网上看的大家都是用的应用公钥,但是我这里怎样都是解签失败,后来问了小码哥才知道要使用支付宝的公钥,下图中标记的地方大家一定要注意
具体的代码如下:
/**
* @Title: alipayNotify
* @Description: 支付宝回调接口
* @author nelson
* @param request
* @param out_trade_no 商户订单号
* @param trade_no 支付宝交易凭证号
* @param trade_status 交易状态
* @throws AlipayApiException
* @return String
* @throws
*/
@RequestMapping("/notify")
@ResponseBody
private String alipayNotify(HttpServletRequest request, String out_trade_no, String trade_no, String trade_status)
throws AlipayApiException {
Map<String, String> map = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
map.put(name, valueStr);
}
// 买家付款账号
// String buyer_logon_id = map.get("buyer_logon_id");
// 交易状态
// trade_status = map.get("trade_status");
// 商户订单号
// out_trade_no = map.get("out_trade_no");
// 支付宝交易号
// trade_no = map.get("trade_no");
//商户编号
// String stord_id = map.get("body");
//充值硬币
// Integer coin_num = Integer.parseInt(map.get("passback_params"));
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(map,aliPayConfig.getAlipay_return_public_key(),
"UTF-8",aliPayConfig.getSign_type());
} catch (AlipayApiException e) {
e.printStackTrace();
return ("fail");// 验签发生异常,则直接返回失败
}
if (signVerified) {
// TODO
//处理你的业务逻辑,更细订单状态等
System.out.println("****************处理业务逻辑,更细订单状态****************");
return ("success");
} else {
return ("fail");
}
}
@RequestMapping("/return")
@ResponseBody
private Map<String, Object> paymentresult(HttpServletRequest request, String out_trade_no, String trade_no, String trade_status)
throws AlipayApiException {
Map<String, Object> resultMap = new HashMap<>();
Map<String, String> map = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
map.put(name, valueStr);
}
// 买家付款账号
// String buyer_logon_id = map.get("buyer_logon_id");
// 交易状态
// trade_status = map.get("trade_status");
// 商户订单号
// out_trade_no = map.get("out_trade_no");
// 支付宝交易号
// trade_no = map.get("trade_no");
//商户编号
// String stord_id = map.get("body");
//充值硬币
// Integer coin_num = Integer.parseInt(map.get("passback_params"));
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(map,aliPayConfig.getAlipay_return_public_key(),
"UTF-8",aliPayConfig.getSign_type());
} catch (AlipayApiException e) {
e.printStackTrace();
resultMap.put("1","验签发生异常");
return resultMap;// 验签发生异常,则直接返回失败
}
if (signVerified) {
// TODO
//处理你的业务逻辑,更细订单状态等
resultMap.put("0","处理成功!");
return resultMap;
} else {
resultMap.put("1","验证失败,不去更新状态!");
return resultMap;
}
}
5.4》最后呢再把Android端的加签代码贴出来
@RequestMapping("/alipayAndroid")
@ResponseBody
public Map<String,Object> getAliPayOrderStr(AlipayVo tOrder) {
Map<String ,Object> resultMap = new HashMap<>();
//最终返回加签之后的,app需要传给支付宝app的订单信息字符串
System.out.println("==================支付宝下单,商户订单号为:"+tOrder.getOut_trade_no());
try{
//实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型),为了取得预付订单信息
AlipayClient alipayClient = new DefaultAlipayClient(aliPayConfig.getGatewayUrL()
, aliPayConfig.getAlipay_app_id(), aliPayConfig.getMerchant_private_key()
, "json","UTF-8",aliPayConfig.getAlipay_public_key()
, aliPayConfig.getSign_type());
//实例化具体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(tOrder.getBody()); //对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。
model.setSubject(tOrder.getSubject()); //商品名称
model.setOutTradeNo(tOrder.getOut_trade_no()); //商户订单号(自动生成)
// model.setTimeoutExpress("30m"); //交易超时时间
model.setTotalAmount(tOrder.getTotal_amount()); //支付金额
model.setProductCode("QUICK_MSECURITY_PAY"); //销售产品码(固定值)
ali_request.setBizModel(model);
ali_request.setNotifyUrl(aliPayConfig.getAlipay_notify_url()); //异步回调地址(后台)
ali_request.setReturnUrl(aliPayConfig.getReturn_url()); //同步回调地址(APP)
// 这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(ali_request); //返回支付宝订单信息(预处理)
String orderString=alipayTradeAppPayResponse.getBody();//就是orderString 可以直接给APP请求,无需再做处理。
resultMap.put("0","加签成功!");
resultMap.put("data",orderString);
} catch (AlipayApiException e) {
e.printStackTrace();
resultMap.put("1","与支付宝交互出错,未能生成订单,请检查代码!");
resultMap.put("data",null);
}
return resultMap;