支付页面

接口文档
java
@Operation(summary="获取订单信息")
@GetMapping("auth/{orderId}")
public Reuslt<OrderInfo> getOrderInfo(@Parameter(name="orderId",description="订单id",required=true)
@PathVaariable Long orderId){
OrderInfo orderInfo = orderInfoService.getOrderInfo(orderId);
return Reuslt.build(orderInfo,ResultCode.SUCCESS);
}
java
@Override
public OrderInfo getOrderInfo(Long orderId){
return orderInfoMapper.getById(orderId);
}
java
//mapper
OrderInfo getById(Long orderId);
xml
<select id="getById"resultType="com.xxx.OrderInfo" >
select *
from order_info
where id=#{orderId}
</select>
不经过购物车,直接购买
java
@Operation(summary="立即购买")
@GetMapping("auth/buy/{skuId}")
public Result buy(@PathVariable Long skuId){
TradeVo tradeVo = orderInfoService.buy(skuId);
return Result.build(tredeVo,ResultCodeEnum.SUCCESS);
}
java
@Override
public TradeVo buy(Long skuId){
List<OrderItem> orderItemList = new ArrayList<>();
ProductSku productSku = productFeignClient.getBySkuId(skuId);
OrderIntem orderItem = new OrderItem();
orderItem.setSkuId(skuId);
orderItem.setSkuName(productSku.getSkuName());
orderItem.setSkuNum(1);
orderItem.setSkuPrice(productSku.getSalePrice());
orderItem.setThumbImg(productSku.getThumbImg());
orderItemList.add(orderItem);
TradeVo tradeVo = new TradeVo();
trade.setOrderItemList(orderItemList);
trade.setTotalAmount(productSku.getSalePrice());
return tradeVo;
}
查询订单
java
@Operation(summary="获取订单分页列表")
@GetMapping("auth/{page}/{limit}")
public Result<PageInfo<OrderInfo>> list(
@Parameter(name="page",description="当前页码",required=true)
@PathVariable Integer page,
@Parameter(name="limit",description="每页记录数",required=true)
@PathVariable Integer limit,
@Parameter(name="orderStatus",description="订单状态",required=false)
@RequestParam(required=false,defaultValue="") Integer orderStatus){
PageInfo<OrderInfo> pageInfo = orderInfoService.findUserPage(page,limit,orderStatus);
return Result.build(pageInfo,ResultCodeEnum.SUCCESS);
}
java
@Override
public PageInfo<OrderInfo> findOrderPage(Integer page,Integer limit,Integer orderStatus){
PageHelper.startPage(page,limit);
//查询订单信息
Long userId = AuthContextUtil.getUserInfo().getId();
List<OrderItem> orderInfoList = orderInfoMapper.findUserPage(userId,orderStatus);
//查询订单里所有的订单项
orderInfoList.forEach(orderInfo -> {
//订单id查询订单里面的订单项
List<OrderItem> orderItemList = orderItemMapper.findByOrderId(orderInfo.getId());
orderInfo.setOrderItemList(orderItemList);
});
return new PageInfo<>(orderInfoList);
}


===================================================================================
支付宝支付


通过营业执照进行开通
获取的参数

①、创建模块service-pay
配置文件、启动类......
yml
spring:
profiles:
active: dev
server:
port:
spring:
application:
name:
cloud:
nacos:
discovery:
server-addr: localhost:8848
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url:
username:
password:
data:
redis:
host: localhost
port: 6379
mybatis:
config-localtion: classpath:mybatis-config.xml
mapper-location: classpath:mapper/*/*.xml
②、保存支付记录
java
public interface PaymetInfoService{
PaymentInfo savePaymentInfo(String orderNo);
}
java
@Service
public class PaymentInfoService implements PaymentInfoService{
@Autowired
private PaymentInfoMapper paymentInfoMapper;
@Override
public PaymentInfo savePaymentInfo(String orderNo){
PaymentInfo paymentInfo = paymentInfoMapper.getByOrderNo(orderNo);
if(paymentInfo==null){
//如果支付记录不存在,则远程调用订单信息
OrderInfo orderInfo = orderFeignClient.getOrderInfoByOrderNo(orderNo).getData();
paymentInfo = new PaymentInfo();
paymentInfo.setUserId(orderInfo.getUserId());
paymentInfo.setPayType(orderInfo.getPayType());
String content = "";
for(OrderItem item:orderInfo.getOrderItemList()){
content += item.getSkuName() + "";
}
paymentInfo.setContent(content);
paymentInfo.setAmount(orderInfo.getTotalAmount());
paymentInfo.setOrderNo(orderNo);
paymentInfoMapper.save(paymentInfo);
}
return paymentInfo;
}
}
java
@Mapper
public interface PaymentInfoMapper{
PaymentInfo getByOrderNo(String orderNo);
void save(PaymentInfo paymentInfo);
}
xml
<sql id="columns">
id,user_id,order_no,pay_type,out_trade_no,amount,content,payment_status,callback_time,callback_content
</sql>
<select id="getByOrderNo" resultMap="paymentInfoMap">
select <include refid="colums"></include>
from payment_info
where order_no = #{orderNo}
</select>
<insert id="save" useGeneratedKeys="true" keyProperty="id">
insert into payment_info(
id,
user_id,
order_no,
pay_type,
out_tread_no,
amount,
coutent,
payment_status,
)
</insert>


③、支付接口
Ⅰ、service-pay模块中添加alipay-sdk依赖
Ⅱ、application-alipay.yml文件,添加配置
Ⅲ、application-dev.yml配置文件需要引入application-alipay.yml
yml
spring:
config:
import: application-alipay.yml
Ⅳ、读取配置文件
java
@Data
@Configuration(prefix="spzx.alipay")
public class AlipayProperties{
//属性名必需和配置文件中的保持一致
private String alipayUrl;
private String appPrivateKey;
public String alipayPublicKey;
private String appId;
public String returnPaymetUrl;
public String notifyPaymentUrl;
public final static String format="json";
public final static String charset="utf-8";
public final static String sign_type="RAS2";
}
Ⅴ、启动类添加注解@EnableConfigurationProperties(value={AlipayProperties.class})
④、配置发送请求的核心对象AlipayClient
java
@Configuration
public class AlipayConfiguration{
@Autowired
private AlipayProperties alipayProperties;
@Bean
public AlipayClient alipayClient(){
AlipayClient alipayClient = new DefaultAlipayClient(
alipayProperties.getAlipayUrl(),
alipayProperties.getAppPrivateKey(),
alipayProperties.format,
alipayProperties.charset,
alipayProperties.getAlipayPublicKey(),
alipayProperties.sign_type
);
return alipayClient;
}
}
⑤、返回支付宝支付页面
java
@Controller
@RequestMapping()
public class AlipayController{
@Autowired
private AlipayService alipayService;
@Operation(summary="支付宝下单")
@GetMapping("submitAlipay/{orderNo}")
public Result<String> submitAliPay(@Parameter(name="orderNo",description="订单号",required=true),
@PathVariable(value="orderNo") String orderNo){
String form = aliapyService.submitAlipay(orderNo);//返回表单页面
return Result.build(form,ResultCodeEnum.SUCCESS);
}
}
java
@Override
public String submitAlipay(String orderNo){
//保存支付记录
PaymentInfo payementInfo = paymentInfoService.savePaymentInfo(orderNo);
//调用支付宝服务接口封装参数
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
//同步回调
alipayRequest.setReturnUrl(alipayProperties.getReturnPaymentUrl());
//异步回调
alipayRequest.setNotifyUrl(alipayProperties.getNotifyPaymentUrl());
//准备请求参数
HashMap<String,Object> map = new HashMap<>();
map.put("out_trade_no",paymentInfo.getOrderNo());
map.put("product_code","QUICK_WAP_WAY");
map.put("total_amount",paymentInfo.getAmount());
//map.put("total_amount",new BigDecimal("0.01")); 为了便于测试
map.put("subject",paymentInfo.getContent());
alipayRequest.setBizContent(JSON.toJSONString(map));
try{
//发送请求
AlipayTradeWapPayResponse response = alipayClient.pageExecute(alipayRequest);
if(response.isSuccess()){
log.info("调用成功");
String form = response.getBody();//返回支付页面
return form;
}else{
log.info("调用失败");
throw new GuiguException(ResultCodeEnum.DATA_ERROR);
}
}catch(AlipayApiException e){
throw new RuntimeException(e);
}
}

⑥、完成支付
- 内网穿透,方便用来开发测试

- 支付成功之后到达的页面:修改自定义的页面
- 本地的接口路径:修改为内网穿透绑定到后端网关接口赠送的域名地址

java
@Operation(summary="支付宝异步回调")
@ReqeustMapping("callback/notify")//该路径和配置文件中的notify_payment_url
@ResponseBody
public String alipayNotify(@RequestParam Map<String,String> paramMap,HttpServletRequest request){
log.info("AlipayController...alipayNotify方法执行了");
boolean signVerified = false;//调用SDK验证签名(验证是否是支付宝传递过来)
try{
signVerified = AlipaySignature.rsaCheckVl(paramMap,
alipayProperties.getAlipayPublicKey(),
AlipayProperties.charset,
AlipayProperties.sign_type);
}catch(AlipayApiException e){
e.printStackTrace();
}
//交易状态
String trade_status = paramMap.get("trade_status");
if(signVerified){
//进行二次校验
if("TRADE_SUCCESS".equals(trade_status)||"TRADE_FINISHED".equals(trade_status)){
//正常的支付成功,更新交易记录状态
paymentInfoService.updatePaymentStatus(paraMap,2);
return "success";
}
}else{
//验签失败,记录异常日志
return "failture";
}
}
java
//支付状态的更新
@Override
public void updatePaymentStatus(Map<String,String> map){
//根据订单编号查询支付记录信息
PaymentInfo paymentInfo = paymentInfoMapper.getByOrderNo(map.get("out_trade_no"));
//如果支付记录已经完成,不需要更新
if(paymentInfo.getPaymentStatus()==1){
return;
}
//没有完成才更新
paymentInfo.setPaymentStatus(1);
paymentInfo.setOutTradeNo(map.get("trade_no"));
paymentInfo.setCallbackTime(new Date());
paymentInfo.set.CallbackContent(JSON.toJSONString(map));
paymentInfoMapper.updatePaymentInfo(paymentInfo);
//远程调用,更新订单模块(根据订单编号修改)
orderFeignClient.updateOorderStatus(paymentInfo.getOrderNo());
//更新sku商品销售
}



配置内网穿透
- 本质上就是因为无法直接调用本地服务,所以通过内网穿透便于第三方服务通过域名来调用本地服务
- ngrok工具:https://ngrok.cc/login/register 注册用户
- 完成实名认证
- 开通隧道管理
- 绑定本地服务开发者的service-gateway网关模块,获取赠送的域名
- 复制隧道id,下载客户端工具windows_amd64zip
