支付宝支付-当面付之扫码支付「扫码支付」
前言
支付宝支付—沙箱环境使用
支付宝支付-支付宝PC端扫码支付
支付宝支付-手机浏览器H5支付
支付宝支付-当面付之扫码支付「本文」
当面付包含两种支付方式:商品条形码支付 + 扫码支付
经过前面两篇PC端扫码支付、手机H5支付,我们可以看到一个共同的特点就是接口返回的都是一个Form表单,然后交给前端提交执行,然后调起支付。其中PC端提交Form表单后跳转至新的窗口进行支付,而手机端H5支付则是唤起支付宝APP支付。
但是现在有这么个场景,我不希望在PC端支付时跳转支付宝网页支付,而是直接将二维码嵌入到系统当中,用户直观看到的就是一个弹窗二维码,然后用户拿支付宝扫码支付…这就是本文的当面付之扫码支付了。
废话不多说,直接进入主题。
Java支付项目实战教程,包括支付宝,微信等支付方式,不看亏!本文开发环境:IDEA + Tomcat8.5 + 支付宝沙箱环境 + SpringBoot
补充:调用沙箱环境接口,需要安装沙箱环境下的支付宝APP,不了解的小伙伴可以参考上方 支付宝支付—沙箱环境使用。
一、pom引入依赖 + 参数准备
引入支付宝支付依赖文件,本文基于4.9.153.ALL版本,及供参考, 最新版本可去官方文档查阅。
<!--alipay-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.9.153.ALL</version>
</dependency>
关于支付宝公钥、私钥、回调地址啥的就不再重复了,不清楚的去看支付宝支付—沙箱环境使用。
配置可以单独创建一个类,静态初始化参数::AlipayConfig.java
public class AlipayConfig {
/** 商户appid **/
public static String APPID = "201610170070";
/** 私钥 pkcs8格式的 **/
public static String RSA_PRIVATE_KEY = "";
/** 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 **/
public static String notify_url = "http://ngrok.sscai.club/alipay_trade_wap_pay_java_utf_8_war_exploded/notify_url.jsp";
/** 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址 **/
public static String return_url = "http://ngrok.sscai.club/alipay_trade_wap_pay_java_utf_8_war_exploded/return_url.jsp";
/** 请求网关地址 **/
public static String URL = "https://openapi.alipaydev.com/gateway.do";
/** 编码 **/
public static String CHARSET = "UTF-8";
/** 返回格式 **/
public static String FORMAT = "json";
/** 支付宝公钥 **/
public static String ALIPAY_PUBLIC_KEY = "";
/** 日志记录目录 **/
public static String log_path = "/log";
/** RSA2 **/
public static String SIGNTYPE = "RSA2";
/** 商户门店编号「需要申请当面付」 **/
public static String STORE_ID = "122211242";
}
几个主要的参数:
- APPID :商户appid
- RSA_PRIVATE_KEY:应用私钥
- ALIPAY_PUBLIC_KEY:支付宝公钥「注意不是应用公钥」
二、后端代码
还是之前的代码,一个controller接口方法,一个具体的实现方法「没有使用开源SDK」:
Controller
@ApiOperation(value = "获取支付宝支付二维码")
public Result<AlipayResponse> getAliPayQrCode(@CurrentUser UserModel user, @RequestBody AlipayOrderRequest alipayOrderRequest ) {
/** 返回给前端的二维码内容 **/
String sHtmlText = null;
try {
/** 判断是否已经存在订单,根据自己业务的不同自行判断**/
if(!StringUtils.isNotBlank(alipayOrderRequest.getWidOutTradeNo())){
Orders orders=ordersService.createOrder(user,alipayOrderRequest.getWidTotalFee(),alipayOrderRequest.getAppType());
alipayOrderRequest.setWidOutTradeNo(orders.getOrderId());
}else{
OrderResponse orders=ordersService.searchOrderDetail(user,alipayOrderRequest.getWidOutTradeNo());
alipayOrderRequest.setWidTotalFee(orders.getAmount().toString());
}
/** 这是主要的方法 **/
sHtmlText = alipayService.alipayOrder(alipayOrderRequest);
} catch (AlipayApiException e) {
e.printStackTrace();
}
AlipayResponse alipayResponse = new AlipayResponse();
alipayResponse.setSHtmlText(sHtmlText);
alipayResponse.setOrderId(alipayOrderRequest.getWidOutTradeNo());
return ResultUtil.success(alipayResponse);
}
ServerImpl
@Transactional
public String alipayOrder(AlipayOrderRequest alipayOrderRequest) throws AlipayApiException {
/**获得初始化的AlipayClient**/
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl,
AlipayConfig.app_id,
AlipayConfig.merchant_private_key,
"json",
AlipayConfig.charset,
AlipayConfig.alipay_public_key,
AlipayConfig.sign_type);
/**当面付之「扫码支付」**/
AlipayTradePrecreateRequest alipayTradePrecreateRequest = new AlipayTradePrecreateRequest();
/**回调地址+异步通知**/
alipayTradePrecreateRequest.setNotifyUrl(AlipayConfig.notify_url);
alipayTradePrecreateRequest.setReturnUrl(AlipayConfig.return_url);
/**商户订单号,商户网站订单系统中唯一订单号,必填**/
String out_trade_no = alipayOrderRequest.getWidOutTradeNo();
/**付款金额,必填**/
String total_amount = alipayOrderRequest.getWidTotalFee();
/**订单名称,必填**/
String subject = alipayOrderRequest.getWidSubject();
/**商户门店编号,必填**/
String store_id = AlipayConfig.STORE_ID;
/**交易超时时间,订单允许的最晚付款时间,必填**/
String timeout_express = "120m";
/**拼接参数**/
alipayTradePrecreateRequest.setBizContent(
"{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"store_id\":\""+ store_id +"\","
+ "\"timeout_express\":\""+timeout_express+"\"}");
return alipayClient.execute(alipayTradePrecreateRequest).getBody();
}
三、返回结果+测试
如上代码调用execute之后的返回结果:
{
"alipay_trade_precreate_response":{
"code":"10000",
"msg":"Success",
"out_trade_no":"20200508160037744742",
"qr_code":"https://qr.alipay.com/bax06173nktjyrrwupss00bf"
},
"sign":"sdk3Q6idlQC+SRuuxc6xXv3g4BAkxEgpA9WYJoiE8oYH5mA6K8+GMwAETNKOhOPh/SoYS4CECzswk/H7qw9A=="
}
我们可以看到qr_code参数了,剩下的就是交给前端生成二维码了,然后用户扫码支付,在这我就不展示了,直接拿链接去草料二维码直接生成个二维码试试。
四、扫码支付回调地址
支付成功后支付宝会回调前边设置的回调地址,由后端的的 return_url 参数控制。
再看看支付成功后的回调接口,由后端的 notify_url 参数控制「没有使用开源的SDK演示」:
public String alipaynotify(Model model, HttpServletRequest request) {
log.info("支付宝异步回调 ------------beg-----------");
String result = "fail";
/**获取支付宝POST过来反馈信息**/
/* *
* 功能:支付宝服务器异步通知页面
* 说明:
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
* 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
Map<String, String> params=this.getAlipayRequest(request);
if(params == null || params.size()==0){
BufferedReader bufferReader = null;
StringBuilder sb = new StringBuilder();
try {
bufferReader = new BufferedReader(request.getReader());
String line = null;
while ((line = bufferReader.readLine()) != null) {
sb.append(new String(line.getBytes("ISO-8859-1"), "utf-8"));
}
} catch (IOException e) {
e.printStackTrace();
}
String body= null;
try {
body = URLDecoder.decode(sb.toString(),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
params=UriComponentsBuilder.newInstance().query(body).build().getQueryParams().toSingleValueMap();
}
boolean signVerified =false;
try {
signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
} catch (AlipayApiException e1) {
log.error("由于"+e1.getErrMsg()+"返回给支付宝系统的结果result:fail");
model.addAttribute("result", "fail");
return result;
}
/**——请在这里编写您的程序(以下代码仅作参考)——**/
/* 实际验证过程建议商户务必添加以下校验:
1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
log.error("支付宝验证签名:---------------------------------"+signVerified);
if(signVerified) {/**验证成功**/
/**商户订单号**/
/**交易状态**/
log.info("支付宝异步回调验签成功!");
String trade_status = params.get("trade_status");
if("TRADE_FINISHED".equals(trade_status)){
/**判断该笔订单是否在商户网站中已经做过处理**/
/**如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序**/
/**如果有做过处理,不执行商户的业务程序**/
/**注意:**/
/**退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知**/
try {
/** 在这里处理支付成功后的操作,比如修改订单状态等等**/
coding...
result = "success";
} catch (Exception e) {
log.error(e.getMessage());
result = "fail";
}
}else if ("TRADE_SUCCESS".equals(trade_status)){
/**判断该笔订单是否在商户网站中已经做过处理**/
/**如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序**/
/**如果有做过处理,不执行商户的业务程序**/
/**注意:**/
/**付款完成后,支付宝系统发送该交易状态通知**/
try {
/** 在这里处理支付成功后的操作,比如修改订单状态等等**/
coding...
result = "success";
} catch (Exception e) {
log.error(e.getMessage());
result = "fail";
}
}else{
result = "fail";
}
}else {/**验证失败**/
result = "fail";
/**调试用,写文本函数记录程序运行情况是否正常**/
/**String sWord = AlipaySignature.getSignCheckContentV1(params);**/
/**AlipayConfig.logResult(sWord);**/
log.debug("支付宝异步回调验签失败");
}
log.debug("异步回调返回给支付宝系统的结果result:"+result);
model.addAttribute("result", result);
log.info("支付宝异步回调 -------------end ------------");
return result;
}
该方法返回给支付宝的 result 就 success、fail 两个结果。
从以上看来,其实不难发现支付宝支付是非常简单的,尽管我上边贴了大量的代码,其实采用开源SDK的话可以更加缩减、美化一些。
ok,这篇文章就到这结束了,上边并没有详细介绍接口调用、参数说明等,详细介绍请查看官方文档:
https://opendocs.alipay.com/open/194/106078/
https://opendocs.alipay.com/open/194/103296
文章最后
博客地址:https://www.cnblogs.com/niceyoo
如果觉得这篇文章有丶东西,不妨关注一下我,关注是对我最大的鼓励~