支付宝官方沙箱文档:电脑网站支付快速接入 - 支付宝文档中心 (alipay.com)
1.准备工作
(1). 先到支付宝开放平台注册账号并登录

(2). 登录后点进控制台,然后往下滑找到沙箱

(3). 选择自定义密钥,并选择公钥模式并点击设置

(4). 下载支付宝开放平台密钥工具(去支付宝开放平台官网里下载),把生成的应用公钥复制到上一步

(5). 创建一个IDEA项目
正式流程:
(1).引入支付宝相关依赖
<!--SDK-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.22.57.ALL</version>
</dependency>
(2). 创建一个AlipayClientConfig类,把之前沙箱里的信息都填进来

(3). 创建一个AlipayController,并调用实现类的tradeCreate方法(自己写的方法,下文有),别忘了加跨域@CrossOrigin注解

(4). tradeCreate实现类代码,里面的orderInfoService要自己创建,里面代码是关于订单信息的
@Service
@Slf4j
public class AlipayServiceImpl implements AlipayService {
@Autowired
private OrderInfoService orderInfoService;
@Transactional
@Override
public String tradeCreate(Long productId) {
try {
log.info("生成订单");
OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId);
//调用支付宝接口
AlipayClient alipayClient = new DefaultAlipayClient(AlipayClientConfig.gateway_url, AlipayClientConfig.app_id, AlipayClientConfig.merchant_private_key, "json", "utf-8", AlipayClientConfig.public_key, "RSA2");
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
//异步接收地址,仅支持http/https,公网可访问
// request.setNotifyUrl("");
//同步跳转地址,仅支持http/https
// request.setReturnUrl("");
/******必传参数******/
JSONObject bizContent = new JSONObject();
//商户订单号,商家自定义,保持唯一性
bizContent.put("out_trade_no", orderInfo.getOrderNo());
//支付金额,最小值0.01元
BigDecimal total = new BigDecimal(orderInfo.getTotalFee().toString()).divide(new BigDecimal("100"));
bizContent.put("total_amount", total);
//订单标题,不可使用特殊符号
bizContent.put("subject", orderInfo.getTitle());
//电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toString());
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
// 如果需要返回GET请求,请使用
// AlipayTradePagePayResponse response = alipayClient.pageExecute(request,"GET");
if(response.isSuccess()){
log.info("调用成功,返回结果 ===> "+ response.getBody());
return response.getBody();
} else {
log.info("调用失败,返回码 ===> "+ response.getCode()+",返回描述 ===> "+response.getMsg());
throw new RuntimeException("创建支付交易失败");
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("创建支付交易失败");
}
}
}
(5). OrderInfoServiceImpl实现类代码(有关订单信息的一些方法)
@Service
@Slf4j
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {
@Resource
private ProductMapper productMapper;
@Override
public OrderInfo createOrderByProductId(Long productId) {
//查找已存在但未支付的订单
OrderInfo orderInfo = this.getNoPayOrderByProductId(productId);
if( orderInfo != null){
return orderInfo;
}
//获取商品信息
Product product = productMapper.selectById(productId);
//生成订单
orderInfo = new OrderInfo();
orderInfo.setTitle(product.getTitle());
orderInfo.setOrderNo(OrderNoUtils.getOrderNo()); //订单号
orderInfo.setProductId(productId);
orderInfo.setTotalFee(product.getPrice()); //分
orderInfo.setOrderStatus(OrderStatus.NOTPAY.getType());
baseMapper.insert(orderInfo);
return orderInfo;
}
/**
* 存储订单二维码
* @param orderNo
* @param codeUrl
*/
@Override
public void saveCodeUrl(String orderNo, String codeUrl) {
QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_no", orderNo);
OrderInfo orderInfo = new OrderInfo();
orderInfo.setCodeUrl(codeUrl);
baseMapper.update(orderInfo, queryWrapper);
}
/**
* 查询订单列表,并倒序查询
* @return
*/
@Override
public List<OrderInfo> listOrderByCreateTimeDesc() {
QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<OrderInfo>().orderByDesc("create_time");
return baseMapper.selectList(queryWrapper);
}
/**
* 根据订单号更新订单状态
* @param orderNo
* @param orderStatus
*/
@Override
public void updateStatusByOrderNo(String orderNo, OrderStatus orderStatus) {
log.info("更新订单状态 ===> {}", orderStatus.getType());
QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_no", orderNo);
OrderInfo orderInfo = new OrderInfo();
orderInfo.setOrderStatus(orderStatus.getType());
baseMapper.update(orderInfo, queryWrapper);
}
/**
* 根据订单号获取订单状态
* @param orderNo
* @return
*/
@Override
public String getOrderStatus(String orderNo) {
QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_no", orderNo);
OrderInfo orderInfo = baseMapper.selectOne(queryWrapper);
if(orderInfo == null){
return null;
}
return orderInfo.getOrderStatus();
}
/**
* 查询创建超过minutes分钟并且未支付的订单
* @param minutes
* @return
*/
@Override
public List<OrderInfo> getNoPayOrderByDuration(int minutes) {
Instant instant = Instant.now().minus(Duration.ofMinutes(minutes));
QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_status", OrderStatus.NOTPAY.getType());
queryWrapper.le("create_time", instant);
List<OrderInfo> orderInfoList = baseMapper.selectList(queryWrapper);
return orderInfoList;
}
/**
* 根据订单号获取订单
* @param orderNo
* @return
*/
@Override
public OrderInfo getOrderByOrderNo(String orderNo) {
QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_no", orderNo);
OrderInfo orderInfo = baseMapper.selectOne(queryWrapper);
return orderInfo;
}
/**
* 根据商品id查询未支付订单
* 防止重复创建订单对象
* @param productId
* @return
*/
private OrderInfo getNoPayOrderByProductId(Long productId) {
QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("product_id", productId);
queryWrapper.eq("order_status", OrderStatus.NOTPAY.getType());
// queryWrapper.eq("user_id", userId);
OrderInfo orderInfo = baseMapper.selectOne(queryWrapper);
return orderInfo;
}
}
(6). 订单表(OrderInfo)和商品表(Product)的实体类代码
@Data
public class OrderInfo extends BaseEntity{
private String title;//订单标题
private String orderNo;//商户订单编号
private Long userId;//用户id
private Long productId;//支付产品id
private Integer totalFee;//订单金额(分)
private String codeUrl;//订单二维码连接
private String orderStatus;//订单状态
}
@Data
public class Product extends BaseEntity{
private String title; //商品名称
private Integer price; //价格(分)
}
(7). 创建的OrderInfoController和ProductController
@CrossOrigin //开放前端的跨域访问
@Api(tags = "商品订单管理")
@RestController
@RequestMapping("/api/order-info")
public class OrderInfoController {
@Resource
private OrderInfoService orderInfoService;
@ApiOperation("订单列表")
@GetMapping("/list")
public R list(){
List<OrderInfo> list = orderInfoService.listOrderByCreateTimeDesc();
return R.ok().data("list", list);
}
/**
* 查询本地订单状态
* @param orderNo
* @return
*/
@ApiOperation("查询本地订单状态")
@GetMapping("/query-order-status/{orderNo}")
public R queryOrderStatus(@PathVariable String orderNo){
String orderStatus = orderInfoService.getOrderStatus(orderNo);
if(OrderStatus.SUCCESS.getType().equals(orderStatus)){
return R.ok().setMessage("支付成功"); //支付成功
}
return R.ok().setCode(101).setMessage("支付中......");
}
}
@CrossOrigin //开放前端的跨域访问
@Api(tags = "商品管理")
@RestController
@RequestMapping("/api/product")
public class ProductController {
@Resource
private ProductService productService;
@ApiOperation("商品列表")
@GetMapping("/list")
public R list(){
List<Product> list = productService.list();
return R.ok().data("productList", list);
}
}
(8). 其他类
public class OrderNoUtils {
/**
* 获取订单编号
* @return
*/
public static String getOrderNo() {
return "ORDER_" + getNo();
}
/**
* 获取退款单编号
* @return
*/
public static String getRefundNo() {
return "REFUND_" + getNo();
}
/**
* 获取编号
* @return
*/
public static String getNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String newDate = sdf.format(new Date());
String result = "";
Random random = new Random();
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
return newDate + result;
}
}
(9). 最后在启动类上加上@EnableScheduling注解,启动便可成功
(10).前端页面只需要写一个js就行了,它会自己有一个页面,通过浏览器解析写入就行了
//productId要自己在前面获取定义,然后直接在购买按钮是调用toPay就行
function toPay() {
//调用支付宝统一收单下单并支付页面接口
fetch(`/api/ali-pay/trade/page/pay/${productId}`,{
method: 'POST',
headers: {
'Content-Type': 'text/html;charset=UTF-8'
}
})
.then(response => response.json())
.then(response =>{
//将支付宝返回的表单字符串写在浏览器中,表单会自动触发submit提交
alert(response.data)
document.write(response.data)
})
}
注意: 如果报错,把数据库订单信息表里的数据删了再试一试
1662

被折叠的 条评论
为什么被折叠?



