首先这是一个好东西,不需要商家认证那些,开发者可以直接整代码并且效果和实际上线效果是一样的。是一些技术研究者的福音。
好了如题,直接上步骤,百度也是遇到了不少坑。
1、首先登陆支付宝https://open.alipay.com
使用扫一扫登陆,然后选择沙箱环境
2、填写一些相关的信息就会进入沙箱应用里面,先别急找参数,先生成公匙和私匙。点击里面的生成方法,会下载一个文件。里面有使用说明。
用里面的方法生成公匙后点击查看应用公匙(RSA2)把生成的公匙填进去。支付宝应用公匙就会自动生成。官方是推荐使用2048位的。
生成的私匙也要保存,但是不用填写在该页面。下面的密匙应用不用管它。
3、提取相关应用参数:注意这个支付宝公匙不是你刚才填写的那个,是旁边系统自动生成的那一个。
4、根据这些参数提取到java应用程序中。我上面的图片是另一个项目的参考,所以回调地址与下面的参数不一致。实际应用中回调地址是要一致的。
在此改正:这是官方文档说明。也就是应用网关 和回调地址 沙箱测试不用设置。避免误导大家,本地部署项目并测试沙箱支付的就不用设置这两个值了,因为这两个值必须保证外网能访问。
因此本地项目测试支付能成功 但是回调不能成功也就没什么好纠结的了。然而我支付宝沙箱页面没设置,只是项目中设置了上面的同步和异步地址还是回调成功了!
- 应用网关:该地址用于接收开放平台的异步通知。目前沙箱环境不需要配置此参数;
- 授权回调地址;第三方应用授权或获取用户信息中用于接收授权回调信息的地址。使用相关产品时再进行配置;
1. 第三方应用授权:
授权url中的redirect_uri必须与此值相同。
2. 获取用户信息:
授权url中的redirect_uri的域名必须与此值相同。(例如:授权回调地址配置:https://auth.example.com/authCallBack 高亮部分需和授权url相同)
- RSA(SHA1)密钥:目前推荐使用RSA2(SHA256)密钥,请参考第1步进行配置;
- AES密钥:目前不再使用;
5、继续下一步,下载沙箱钱包。不过有点坑,我用手机扫一扫没能下载,是在电脑端下载了再安装到手机上的。
6、下载安装了沙箱钱包。还要解决登录账号的问题,这点支付宝都没有直接说明有点奇葩。
https://openhome.alipay.com/platform/appDaily.htm?tab=account
进这个地址,支付宝扫一扫就有了,使用下面的账号登录测试支付就好了
7、测试成功
支付宝沙箱环境还有一个很大的功能,那就是余额,你的沙箱支付宝余额几百万。
你把这余额给老丈人、小妹妹一看,瞬间被你征服啊。真是程序员泡妞把妹的神器啊,哈哈哈!
下面把支付宝支付的代码贴一下,萌新可以看看,也就是两个类。有时候失败可能是沙箱设置的参数和项目不一致,公私匙。因为实际线上环境就有重新生成公私匙解决了问题的例子。
配置文件类AlipayConfig,这个自己换成沙箱支付的请求网关:https://openapi.alipaydev.com/gateway.do
package com.sanyi.qibaobusiness.framework.payment.ali.config;
public class AlipayConfig {
//发起请求的应用ID。沙箱与线上不同,请更换代码中配置;
public static String app_id ="";
//支付宝私匙
public static String merchant_private_key = "";
//支付宝公匙
public static String alipay_public_key = "";
//服务器异步通知路径
public static String notify_url = "http://127.0.01/alipay/alipayNotifyNotice.action";
//服务器同步通知路径
public static String return_url = "http://127.0.0.1/alipay/alipayReturnNotice.action";
//公匙类型/签名类型
public static String sign_type = "RSA2";
//编码格式
public static String charset = "utf-8";
//向支付宝发起请求的网关。沙箱与线上不同,请更换代码中配置;沙箱:https://openapi.alipaydev.com/gateway.do上线https://openapi.alipay.com/gateway.do
public static String gatewayUrl = "https://openapi.alipay.com/gateway.do";
}
View Code
AlipayController发起支付请求的类
package com.sanyi.qibaobusiness.framework.payment.ali.controller;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* 阿里支付
*/
@Controller
@RequestMapping("/alipay")
public class AlipayController {
final static Logger log = LoggerFactory.getLogger(AlipayController.class);
@Resource
private RentingtoolsBiz rentingtoolsBiz; //暂时只有出租工具的订单在这里支付
@Resource
private CustomerOrderBiz customerOrderBiz;
@Resource
private BrowseRecordsCommonUtil browseRecordsCommonUtil;
/**
* 前往支付宝第三方网关进行支付
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping(value = "/goAlipay", produces = "text/html; charset=UTF-8")
@ResponseBody
public String goAlipay( HttpServletRequest request, HttpServletRequest response) throws Exception {
BrowseRecordsUtil browseRecordsUtil= browseRecordsCommonUtil.getBrowseRecords(request);
Order order=browseRecordsUtil.getPayOrder();
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
String out_trade_no =null;
//付款金额,必填
String total_amount = null;
//订单名称,必填
String subject = null;
//商品描述,可空
String body = null;
// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
String timeout_express = "1c";
if(null!=total_amount){ //支付金额不等于空
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"timeout_express\":\""+ timeout_express +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求
String result = alipayClient.pageExecute(alipayRequest).getBody();
return result;
}
return "订单信息错误!";
}
/**
* 支付宝同步通知页面,成功返回首页
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping(value = "/alipayReturnNotice")
public String alipayReturnNotice(Model model,HttpServletRequest request, HttpServletRequest response) throws Exception {
log.info("支付成功, 进入同步通知接口...");
//获取支付宝GET过来反馈信息
Map<String,String> params = new HashMap<String,String>();
Map<String,String[]> requestParams = request.getParameterMap();
for (Iterator<String> 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] + ",";
}
//乱码解决,这段代码在出现乱码时使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名
//——请在这里编写您的程序(以下代码仅作参考)——
if(signVerified) {
//商户订单号
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 total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
int result=0;
//这里根据自身的业务写代码,我这里删掉了
if(result==0){
model.addAttribute("resultinfo","更新订单失败!请业务员联系后台管理员!");
}else {
model.addAttribute("resultinfo","出租工具成功!");
}
log.info("********************** 支付成功(支付宝同步通知) **********************");
log.info("* 订单号: {}", out_trade_no);
log.info("* 支付宝交易号: {}", trade_no);
log.info("* 实付金额: {}", total_amount);
log.info("***************************************************************");
}else {
log.info("支付, 验签失败...");
}
return "/business/index";//成功返回首页
}
/**
* 支付宝异步 通知页面
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping(value = "/alipayNotifyNotice")
@ResponseBody
public String alipayNotifyNotice(HttpServletRequest request, HttpServletRequest response) throws Exception {
log.info("支付成功, 进入异步通知接口...");
//获取支付宝POST过来反馈信息
Map<String,String> params = new HashMap<String,String>();
Map<String,String[]> requestParams = request.getParameterMap();
for (Iterator<String> 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] + ",";
}
//乱码解决,这段代码在出现乱码时使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名
//——请在这里编写您的程序(以下代码仅作参考)——
/* 实际验证过程建议商户务必添加以下校验:
1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
if(signVerified) {//验证成功
//商户订单号
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");
//付款金额 到这里获取到这些信息就可以了,下面的不用看
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
if(trade_status.equals("TRADE_FINISHED")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意: 尚自习的订单没有退款功能, 这个条件判断是进不来的, 所以此处不必写代码
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
}else if (trade_status.equals("TRADE_SUCCESS")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//付款完成后,支付宝系统发送该交易状态通知
int result=0;
//这里根据自身的业务写代码,我这里删掉了
if(result==0){
log.info("resultinfo","更新订单失败!请业务员联系后台管理员!");
}else {
log.info("resultinfo","出租工具成功!");
}
log.info("********************** 支付成功(支付宝同步通知) **********************");
log.info("* 订单号: {}", out_trade_no);
log.info("* 支付宝交易号: {}", trade_no);
log.info("* 实付金额: {}", total_amount);
log.info("***************************************************************");
}
log.info("支付成功...");
}else {//验证失败
log.info("支付, 验签失败...");
}
return "success";
}
}
View Code