<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>确定订单</title>
</head>
<body>
<dev>
您的订单信息如下,确认无误之后支付<br/>
*******************订单详情*********
<form>
<button>确定支付</button>
</form>
</dev>
</body>
</html>
return.html返回页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付成功</title>
</head>
<body>
<dev>
您已经购买了此商品,感谢支付
</dev>
</body>
</html>
4. 配置application.yml中的详细信息文件
5. 初始化SDK
package com.atguigu.springcloud.init;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import org.springframework.stereotype.Component;
@Component
public class ProjectInit implements ApplicationRunner {
@Value("${alipay.appId}")
private String appId;
@Value("${alipay.privateKey}")
private String privateKey;
@Value("${alipay.publicKey}")
private String publicKey;
@Value("${alipay.gateway}")
private String gateway;
@Value("${alipay.returnurl}")
private String returnurl;
@Value("${alipay.notifyUrl}")
private String notifyUrl;
@Override
public void run(ApplicationArguments args) throws Exception {
Factory.setOptions(getOptions());
System.out.println("*******************支付宝SDK初始化**************");
}
private Config getOptions() {
Config config = new Config();
config.protocol = "https";
config.gatewayHost = this.gateway;
config.signType = "RSA2";
config.appId = this.appId;
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
config.merchantPrivateKey = this.privateKey;
//注:证书文件路径支持设置为文件系统中的路径或CLASS_PATH中的路径,优先从文件系统中加载,加载失败后会继续尝试从CLASS_PATH中加载
config.merchantCertPath = this.publicKey;
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
config.alipayPublicKey = this.publicKey;
//可设置异步通知接收服务地址(可选)
config.notifyUrl = this.notifyUrl;
return config;
}
}
package com.atguigu.springcloud.entities;
import lombok.Data;
import java.math.BigDecimal;
/**
* 发起支付时的参数
*/
@Data
public class PaymentBO {
//省略其他的业务参数,如商品id、购买数量等
//商品名称
private String subject;
//总金额
private BigDecimal total = BigDecimal.ZERO;
}
package com.atguigu.springcloud.until;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
//生成订单必传参数
public class OrderUtil {
/**
* 根据时间戳生成订单号
*/
public static String getOrderNo() {
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = Instant.ofEpochMilli(System.currentTimeMillis()).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
return df.format(localDateTime);
}
}
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.PaymentBO;
import com.atguigu.springcloud.until.OrderUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import java.math.BigDecimal;
@Service
public class PayService {
//支付成功后要跳转的页面
@Value("${alipay.returnurl}")
private String returnurl;
/**
* 下单支付
*/
public Object pay(PaymentBO bo) throws Exception {
//从存储介质(如MySQL、Redis)查询商品信息、总金额等敏感信息
//…………省略相关代码,这里直接赋值…………
bo.setSubject("支付测试商品");
bo.setTotal(new BigDecimal(10.00));
//调用sdk,发起支付
AlipayTradePagePayResponse response = Factory.Payment
//选择网页支付平台
.Page()
//调用支付方法,设置订单名称、我们自己系统中的订单号、金额、回调页面
.pay(bo.getSubject(), OrderUtil.getOrderNo(), bo.getTotal().toString(), returnurl);
//这里的response.body,就是一个可以直接加载的html片段,
// 这里为了简单我直接返回这个片段,前端直接
//这是app支付
// AlipayTradeAppPayResponse pay = Factory.Payment.App().pay(bo.getSubject(), OrderUtil.getOrderNo(), bo.getTotal().toString());
return response.body;
}
}
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.PaymentBO;
import com.atguigu.springcloud.service.PayService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
1. 支付宝支付,控制器
2. 3. @author wangziyang
*/
@RestController
@RequestMapping(value = "/pay")
@AllArgsConstructor
public class PayController {
private PayService payService;
/**
* 下单支付
*/
@GetMapping(value = "/confirm", produces = {"text/html;charset=UTF-8"})
public Object pay(@RequestParam(required = false) PaymentBO bo) throws Exception {
//这个接口其实应该是post方式的,但是我这里图方便,直接以get方式访问,
//且返回格式是text/html,这样前端页面就能直接显示支付宝返回的html片段
//真实场景下由post方式请求,返回code、msg、data那种格式的标准结构,让前端拿到data里的
//html片段之后自行加载
//由于我这里并没有真正的传参数,所以象征性的new一下,避免空指针
bo = new PaymentBO();
return payService.pay(bo);
}
}
支付完成后进行回调
参数有很多,我就不一一列出了。主要的参数是商户订单号(即我们自己生成的订单号)和交易状态, 我们根据订单号是否交易成功,进行后面的业务逻辑,比如给用户加积分、将订单状态变为已支付、发送业务短信、通知商家接单……等等。
参数很多,所以我就不新建实体类了,简单的用Map接一下,在PayController新增以下接口(注意是post方式)
/**
* 支付成功的回调
*/
@PostMapping(value = "/notify")
public Object fallback(HttpServletRequest request) {
Map map = request.getParameterMap();
System.out.println("进入了回调" + map);
//回调之后,可以获取订单号,订单状态可以改成已经支付的状态、
// 给用户增加积分
//告知快递进行发货
//扣除库存等等操作在此之后完成
return null;
}