一、支付宝沙箱环境电脑支付demo

1、打开电脑网站支付文档

https://open.alipay.com/search/searchDetail.htm?tabType=doc&keyword=%E7%94%B5%E8%84%91%E7%BD%91%E7%AB%99%E6%94%AF%E4%BB%98

2、下载demo

下载支付demo的代码
https://opendocs.alipay.com/open/270/106291

3、沙箱配置

https://openhome.alipay.com/platform/appDaily.htm?tab=info

第1步:公钥和私钥介绍

公钥加密私钥解密,私钥加密公钥解密

对称加密:加密和解密是相同的密钥
非对称加密:加密和解密是不同的密钥

签名:防止传输中途数据被篡改
例如:媒婆给张三介绍了一个女朋友,为了保证女朋友在接送路途中不会被其他人偷偷的享用,媒婆给女朋友的身体加了朱砂红,如果朱砂红没了,说明张三女朋友的贞洁就没了,这样可以有效证明女朋友在路途中是否被人劫持过

第2步:生成密码

https://miniu.alipay.com/keytool/create

第3步:沙箱设置公钥

通过生成秘钥工具,生成商户公钥和私钥,在沙箱设置商户公钥后,会自动生成支付宝公钥,支付宝私钥由支付宝保存

4、配置

public class AlipayConfig {// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号public static String app_id = "";// 商户私钥,您的PKCS8格式RSA2私钥public static String merchant_private_key = "";// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。public static String alipay_public_key = "";// 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问public static String notify_url = "http://工程公网访问地址/alipay.trade.page.pay-JAVA-UTF-8/notify_url.jsp";// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问public static String return_url = "http://工程公网访问地址/alipay.trade.page.pay-JAVA-UTF-8/return_url.jsp";// 签名方式public static String sign_type = "RSA2";// 字符编码格式public static String charset = "utf-8";// 支付宝网关public static String gatewayUrl = "https://openapi.alipay.com/gateway.do";

二、项目整合支付

1、导入pom

     <!--整合阿里支付--><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.10.81.ALL</version></dependency>

2、工具类

package com.lian.gulimall.order.config;import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.lian.gulimall.order.vo.PayVo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@ConfigurationProperties(prefix = "alipay")
@Component
@Data
public class AlipayTemplate {//在支付宝创建的应用的idprivate String app_id = "2021000117639656";// 商户私钥,您的PKCS8格式RSA2私钥private String merchant_private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCQiQdxS5aUkeO0ue8rlmZ/TinGY47457fCQoW4egyDaNcm3KqH+RSr1gD3+UNabL2B94dwSe85IAmN3eJRu98Oq44Jko73HGfZG1GtqxEN2/Xbu3fQUNhczLUPtD9SYZFD+i/bi6z+OwRcUJi8SM4cWtaYHcdZ6UUKkFRqc/PAqtTZ+xlDuMtPsb7ZU47w9MT+x2LDIxw0N22VNhJrDpe9RsOYnUO5+PrAYuKIJIVE/4g5kp4P5xHBk71sbobg/6Rv1tOFr2vxJuBaizgGUEMLMt5nRRiFqiil7Qm7DX2MXkDyasHdZRY8S5YhrZ1OqhLktPQ4DAVRbFu0ngzR8AHnAgMBAAECggEAVscvBMLRYdpF0Nhh1NW0LHMNeeOEj8Tv+15dUulUYgGsS9TpPkI6OwF+C2RJIB1xmnq3gp+hVbb6hYbWvyQBRJmT6gNoes0/PIa+f3rZ/mw2LPtdcGFZxj3R59/ctOWHhjzMRxCRiQJ+F778NO+xcCzJ8wLOEhmQLkuBo3iCvQXsVIrlQdFQqioQ2ujQXNbN0sAZzTLUQDIY/yCu/BUOHm45qTPFw0EikfLJm6Ntf6+/R6WjA8BaHT5vXMm0LENx8OPxw2gTm412R9nDLHTOOTHnVgTWKxeDaems6Dj8VVkLI78lJjtOXSoCYxWT6i2+afe6c5RpPm5XAMjg/+h5YQKBgQDTBrwVSLfud2FL9ERhcQALaE5uHnItXKt+hfwmsixTBKRSKW8onpNxxWZa2ABBQMSuPjtngU97ZAIpf4z4plFicWj2oqtX/PJpWTovpbPCfkFYPTbNFDyqhQni6nYIFXCNndb6M/q4lzvvJ6R6V018CiV7M5RRRg/mWxyhH8c08wKBgQCvVqW4qW1DaOMMqlZ9H4QF2sYCetBprrmz31tZH3iR4uVFy6J+KVOlA7IajYXSr9nxsZuxBJBd1KAzEX18fnM/IQdwvAUMGNcNVp+h099klmpZ2HgHn961KQ1ENbWXW+FjTQPwOXuDCdm0/yL8MRk8G/io6A57bFsl2fY0pk8MPQKBgGfz/VsOw2y5ajicT8Mgg0EbVOdyIBSwfdqJ7jixtJwAwWGn/5Ym58M31sARf66lVT3wchr+3ESQc3A14R/r7lNsQqScZYOmm1PCHKO0IOVChLYPEZvcyDXH66LYlOROi293vZrrWCrudRvOj3uk3gqohOY39mxC8zoqz+OfX+Z5AoGAMrOkkkRz8wU2eqgbAG2HmMXNclQfRiAfVPMmkBv4mn74+3jKER9UL6hk7Sa1Ztow9KH+Om7ubCOa+4l4NvK90qhzZVcJQplAPnwpBvukYorYUAi3lMTW+p3XVz0MuKr5/uP7WpDUtWy4xozsie6MtIQGIJmR7oDDzqs7dbgNraUCgYEApoMOTeNQ9vAvvLnzlLPNONy8wNoHnptDg9g9AAlIYnpDzGu5YxeqqS/+MLee+7tsykIDqX9MPnZVvjLLjFhTLjGhTY5sATqFtfEN+rPSSKubcxiCVeu4DaR57q8x1fHTHCxvScgfr+gz/aOOP/p6wB7PclDU89ZRqoCiSG4WIk8=";// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。private String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgzxZ/75S8h0rDR/KinN1nCGMykp+kUymAFBmOeivFjgZpAQshbxV45LDf+V/YC/vg+mdiZntdqbwI5D/1zZhgvyPi+3PDyE56JqLDpDvNktRrHScI8YGm8h0qiQVzHAsgAAjs4fvbl0AUdEENvwRUiVkq+5BFwzl3RDjXR1ofq52km4e/1XccfK0Wwxyu0zYIMwm+73HEPNlZk/3fF1fqpd86Za0sRZh83B8XWwzL2WnZyGH+GapKXPpWGvyNsSR9vafBBxlI1wS2nq0UEAUqH95VhKu2gXb4VnaJNgYTM/OIE+HdxdYULEb8ohb15/sCkmRtp24gxcxGc+qeIfyjQIDAQAB";// 服务器[异步通知]页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问// 支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息private String notify_url = "http://47.110.91.150/payed/notify";// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问//同步通知,支付成功,一般跳转到成功页private String return_url = "http://member.gulimall.com/memberOrder.html";// 签名方式private String sign_type = "RSA2";// 字符编码格式private String charset = "utf-8";// 支付宝网关; https://openapi.alipaydev.com/gateway.doprivate String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";//public String pay(PayVo vo) throws AlipayApiException {//AlipayClient alipayClient = new DefaultAlipayClient(AlipayTemplate.gatewayUrl, AlipayTemplate.app_id, AlipayTemplate.merchant_private_key, "json", AlipayTemplate.charset, AlipayTemplate.alipay_public_key, AlipayTemplate.sign_type);//1、根据支付宝的配置生成一个支付客户端AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,app_id, merchant_private_key, "json",charset, alipay_public_key, sign_type);//2、创建一个支付请求 //设置请求参数AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();alipayRequest.setReturnUrl(return_url);alipayRequest.setNotifyUrl(notify_url);//商户订单号,商户网站订单系统中唯一订单号,必填String out_trade_no = vo.getOut_trade_no();//付款金额,必填String total_amount = vo.getTotal_amount();//订单名称,必填String subject = vo.getSubject();//商品描述,可空String body = vo.getBody();alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","+ "\"total_amount\":\""+ total_amount +"\","+ "\"subject\":\""+ subject +"\","+ "\"body\":\""+ body +"\","+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");String result = alipayClient.pageExecute(alipayRequest).getBody();//会收到支付宝的响应,响应的是一个页面,只要浏览器显示这个页面,就会自动来到支付宝的收银台页面System.out.println("支付宝的响应:"+result);return result;}
}

3、PayVo

import lombok.Data;@Data
public class PayVo {private String out_trade_no;  // 商户订单号 必填private String subject;       // 订单名称 必填private String total_amount;  // 付款金额 必填private String body;          // 商品描述 可空
}

4、控制层

前台点击支付,跳转此页面,http://order.gulimall.com/payOrder?orderSn

package com.lian.gulimall.order.web;import com.lian.gulimall.order.config.AlipayTemplate;
import com.lian.gulimall.order.service.OrderService;
import com.lian.gulimall.order.vo.PayVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;/*** 支付类*/
@Controller
public class PayWebController {@AutowiredOrderService orderService;@AutowiredAlipayTemplate alipayTemplate;/*** 点击支付跳转此页面,http://order.gulimall.com/payOrder?orderSn* 明确表示产生哪个类型的数据,不是json,是text/html* 1、让浏览器展示支付页* 2、支付成功后,要跳到用户的订单列表页*/@ResponseBody@GetMapping(value = "/payOrder",produces = "text/html")public String payOrder(@RequestParam("orderSn") String orderSn) throws Exception {//根据订单号返回 PayVo 类PayVo payVo = orderService.getOrderPay(orderSn);String pay = alipayTemplate.pay(payVo);System.out.println(pay);return pay;}
}

5、业务类

//根据订单号返回 PayVo 类
//PayVo payVo = orderService.getOrderPay(orderSn);@Overridepublic PayVo getOrderPay(String orderSn) {//根据订单号查询到此订单OrderEntity orderEntity = baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));PayVo vo = new PayVo();//订单号vo.setOut_trade_no(orderEntity.getOrderSn());//订单的主题//根据订单号查询出所有的订单项List<OrderItemEntity> orderItemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));OrderItemEntity entity = orderItemEntities.get(0);vo.setSubject(entity.getSkuName());//订单备注,设置为商品的销售属性vo.setBody(entity.getSkuAttrsVals());//获取订单总额且设置小数点向上浮动2位BigDecimal payAmount = orderEntity.getPayAmount().setScale(2, RoundingMode.UP);//订单的总价vo.setTotal_amount(payAmount.toString());return vo;}

6、支付成功跳转到用户订单列表页

阿里支付的 alipayTemplate中,支付成功后,跳转到会员服务的此页面,展示所有的订单列表
String return_url = “http://member.gulimall.com/memberOrder.html”;

package com.lian.gulimall.member.web;import com.alibaba.fastjson.JSON;
import com.lian.common.utils.R;
import com.lian.gulimall.member.feign.OrderFeiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;import java.util.HashMap;
import java.util.Map;/*** 阿里支付的 alipayTemplate中,支付成功后,跳转到会员服务的此页面,展示所有的订单列表* String return_url = "http://member.gulimall.com/memberOrder.html";** 用拦截器实现筛选只有登录用户才可以访问用户服务的页面*/
@Controller
public class MemberWebController {@AutowiredOrderFeiService orderFeiService;@GetMapping("/memberOrder.html")public String memberOrderPage(@RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum, Model model){Map<String,Object> page = new HashMap<>();//第几页page.put("page",pageNum.toString());//查询当前登录的用户的所有订单列表数据,此处是feign远程调用,会丢失请求头,所以一定要配置 feign//因为R r = orderFeiService.listWithItem(page);model.addAttribute("orders",r);System.out.println(JSON.toJSONString(r));//支付成功跳转到列表页return "orderList.html";}
}

7、查询用户订单列表

远程调用订单服务,查询当前登录的用户的所有订单列表

package com.lian.gulimall.member.feign;import com.lian.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;import java.util.Map;@FeignClient("gulimall-order")
public interface OrderFeiService {/*** 查询订单详情,订单列表页,分页里 包含 每个用户的订单,每个订单里又封装了 多个订单列表*/@PostMapping("/order/order/listWithItem")R listWithItem(@RequestBody Map<String,Object> params);}

8、订单服务的控制层

 /*** 分页查询当前登录用户的所有订单* 查询订单详情,订单列表页,分页里 包含 每个用户的订单,每个订单里又封装了 多个订单列表* 请求体@RequestBody 必须是 postMapping*/@PostMapping("/listWithItem")public R listWithItem(@RequestBody Map<String,Object> params){//查询当前用户的订单详情PageUtils page = orderService.queryPageWithItem(params);return R.ok().put("page",page);}

9、订单服务的实现类

 @Overridepublic PageUtils queryPageWithItem(Map<String, Object> params) {//所有进入订单页面的人必须登录MemberRespVo memberRespVo = LoginUserIntereptor.loginUser.get();//分页,每个用户的订单IPage<OrderEntity> page = this.page(new Query<OrderEntity>().getPage(params),new QueryWrapper<OrderEntity>().eq("member_id",memberRespVo.getId()).orderByDesc("id"));//将订单表中各个订单都添加订单项后,返回列表List<OrderEntity> entities = page.getRecords().stream().map((order) -> {//根据订单号查询出所有的 订单详情实体类List<OrderItemEntity> list = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", order.getOrderSn()));//为每个订单号封装订单详情order.setOrderItemEntities(list);return order;}).collect(Collectors.toList());//分页里封装 一堆订单,每个订单又封装了一堆订单项列表page.setRecords(entities);return new PageUtils(page);}

10、会员服务配置SpringSession

pom

     <!--引入分布式session--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>

配置文件

#session中存储的类型
spring.session.store-type=redis
#redis主机
spring.redis.host=192.168.56.10

主启动类加注解

@EnableRedisHttpSession  //开启SpringSession

SpringSession配置

package com.lian.gulimall.member.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;/*** 1、spring-session依赖* 2、spring-session配置* 3、引入LoginUserInterceptor拦截器 、 WebMvcConfigure*/
@Configuration
public class GulimallSessionConfig {/*** 设置 cookie的域名范围* 设置cookie 名称* @return*/@Beanpublic CookieSerializer cookieSerializer(){DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();cookieSerializer.setDomainName("gulimall.com");cookieSerializer.setCookieName("GULIMALL");return cookieSerializer;}/*** 设置保存到 redis 中的数据不乱码,将其序列化为json格式* @return*/@Beanpublic RedisSerializer<Object> springSessionRedisSerializer(){return new GenericJackson2JsonRedisSerializer();}
}

feign远程调用丢失请求头,需要配置

package com.lian.gulimall.member.config;import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;/*** 解决feign远程调用丢失请求头问题* 给容器中添加一个拦截器*/
@Configuration
public class GulifeignConfig {@Bean("requestInterceptor")public RequestInterceptor requestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {//1、使用RequestContextHolder 获取当前request请求携带的所有属性(包含cookie、session、请求头等)// 发送toTrade请求 ——》 先进入拦截器过滤//ServletRequestAttributes 是 RequestAttributes 的子类ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();if (attributes != null){System.out.println("拦截器线程:"+Thread.currentThread().getId());//获取老请求HttpServletRequest request = attributes.getRequest();//2、同步请求头数据if (request != null){//老请求中根据Cookie头获取 Cookie值String cookie = request.getHeader("Cookie");if (cookie != null){//新请求中设置请求头里,添加 cookie头和cookie值,给新请求中把老请求的cookie同步了template.header("Cookie",cookie);}}}}};}
}

因为会员服务必须是登录用户才可操作,所以配置拦截器,运行登录用户才可操作

package com.lian.gulimall.member.interceptor;import com.lian.common.constant.AuthServerConstant;
import com.lian.common.vo.MemberRespVo;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 拦截器* 浏览器发过来请求后 —— 进入controller的请求后 —— 直接进入拦截器 —— 然后再继续执行 controller 后的方法*/
@Component
public class LoginUserInterceptor implements HandlerInterceptor {public static ThreadLocal threadLocal = new ThreadLocal<MemberRespVo>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求路径String uri = request.getRequestURI();AntPathMatcher antPathMatcher = new AntPathMatcher();boolean match = antPathMatcher.match("/member/**", uri);    //拦截器放行此路径if (match){return true;}MemberRespVo attribute = (MemberRespVo) request.getSession().getAttribute(AuthServerConstant.LOGIN_USER);if (attribute != null){threadLocal.set(attribute);//true 代表放行return true;}else {request.getSession().setAttribute("msg","请先进行登录");response.sendRedirect("http://auth.gulimall.com/login.html");//没有登录就拦截return false;}}
}

拦截器配置类

@Configuration
public class MemberWebConfig implements WebMvcConfigurer {@AutowiredLoginUserInterceptor loginUserInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//拦截:会员服务里的所有controller功能registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**");}
}

到这步,就可以跳转到订单链表页了

11、异步通知内网穿透环境搭建

订单支付成功后,支付宝会异步通知我们订单支付成功的信息,因为是跨服务,所以必须要设置内网穿透环境

package com.lian.gulimall.order.listener;import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.util.Map;/*** 订单支付成功后,支付宝会多次通知我们订单支付成功的信息* 我们要回复支付宝 success,支付宝才会停止通知**  支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息*  private String notify_url = "http://47.110.91.150/payed/notify";*/
@RestController
public class OrderPayedListener {@PostMapping("/payed/notify")public String handlerAlipayed(HttpServletRequest request){//获取所有的请求参数Map<String, String[]> map = request.getParameterMap();System.out.println("支付宝通知完成,数据是:"+map);//只要我们收到支付宝给我们的异步通知,订单支付成功,就返回给支付宝 success,支付宝就不会再通知 post形式return "success";}
}

配置内网穿透

// 支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息
private String notify_url = "http://0jrtp5ds8n.52http.tech/payed/notify";


首页访问成功

nginx配置精准访问地址


需要配置域名地址,不然默认是从localhost访问

拦截器中配置,订单成功后,对阿里异步请求通知的地址放行

package com.lian.gulimall.order.interceptor;import com.lian.common.constant.AuthServerConstant;
import com.lian.common.vo.MemberRespVo;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 拦截器* 用户订单服务必须要登录才可以使用,所以用拦截器* 拦截器作用:* 1、只对控制层起作用* 2、浏览器发出请求——触发指定控制层请求——立刻跳到拦截器——拦截器过滤后再继续执行控制层controller中的方法*/
@Component
public class LoginUserIntereptor implements HandlerInterceptor {//threadLocal 作用是防止多个线程同时访问同一变量引发线程不安全问题,同一个线程共享数据,类似map(key,value),只有key不同,值才会不一样public static ThreadLocal<MemberRespVo> loginUser = new ThreadLocal<>();/*** 控制层目标方法执行之前* @GetMapping("/toTrade") 方法之前执行,过滤器完成后再执行 return "confirm";* @param request* @param response* @param handler* @return 如果 return true代表放行,return false 代表拦截* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {/*** 拦截器放行指定路径 order/order/status/{orderSn}*/String uri = request.getRequestURI();AntPathMatcher antPathMatcher = new AntPathMatcher();boolean match = antPathMatcher.match("/order/order/status/**", uri);//阿里异步请求通知,订单已经成功的消息boolean match1 = antPathMatcher.match("/payed/notify", uri);if (match || match1){       //如果匹配就放行return true;}//获取session//从session中查找是否保存了 登录用户MemberRespVo attribute = (MemberRespVo)request.getSession().getAttribute(AuthServerConstant.LOGIN_USER);//如果根据loginUser查询到数据不为空,说明已登录if (attribute != null){//将已登录数据保存到 threadLocal中loginUser.set(attribute);//登录成功后 放行,继续执行controller中的 跳转到 订单确认页面return true;}else {//如果没有登录成功,将提示信息保存到session中,提醒用户进行登录request.getSession().setAttribute("msg","请先进行登录");//响应端重定向到登录页面response.sendRedirect("http://auth.gulimall.com/login.html");//没登录就拦截return false;}}
}

12、支付成功回调

支付成功后,支付宝会返回一个异步vo

@ToString
@Data
public class PayAsyncVo {private String gmt_create;private String charset;private String gmt_payment;private String notify_time;private String subject;private String sign;private String buyer_id;//支付者的idprivate String body;//订单的信息private String invoice_amount;//支付金额private String version;private String notify_id;//通知idprivate String fund_bill_list;private String notify_type;//通知类型; trade_status_syncprivate String out_trade_no;//订单号private String total_amount;//支付的总额private String trade_status;//交易状态  TRADE_SUCCESSprivate String trade_no;//流水号private String auth_app_id;//private String receipt_amount;//商家收到的款private String point_amount;//private String app_id;//应用idprivate String buyer_pay_amount;//最终支付的金额private String sign_type;//签名类型private String seller_id;//商家的id}

根据异步vo,改变订单状态

package com.lian.gulimall.order.listener;import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.lian.gulimall.order.config.AlipayTemplate;
import com.lian.gulimall.order.service.OrderService;
import com.lian.gulimall.order.vo.PayAsyncVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;/*** 订单支付成功后,支付宝会多次通知我们订单支付成功的信息* 我们要回复支付宝 success,支付宝才会停止通知**  支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息*  private String notify_url = "http://47.110.91.150/payed/notify";*/
@RestController
public class OrderPayedListener {@AutowiredOrderService orderService;@AutowiredAlipayTemplate alipayTemplate;/*** 支付宝自动会发送异步请求,通知订单成功,封装通知数据到一个vo里* @param vo* @param request* @return*/@PostMapping("/payed/notify")public String handlerAlipayed(PayAsyncVo vo, HttpServletRequest request) throws Exception {//获取所有的请求参数
//        Map<String, String[]> map = request.getParameterMap();
//        System.out.println("支付宝通知完成,数据是:"+map);//获取所有请求的key
//        Set<String> set = map.keySet();
//        for (String key : set) {//            //获取每个key对应的参数
//            String value = request.getParameter(key);
//            System.out.println("参数名:"+key+"参数值:"+value);
//        }/*** 验签:验证是否是支付宝给我们发回来的数据*///获取支付宝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);}//调用SDK验证签名boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayTemplate.getAlipay_public_key(), alipayTemplate.getCharset(), alipayTemplate.getSign_type());//——请在这里编写您的程序(以下代码仅作参考)——if (signVerified){System.out.println("验签成功");String result = orderService.handlePayResult(vo);//只要我们收到支付宝给我们的异步通知,订单支付成功,就返回给支付宝 success,支付宝就不会再通知 post形式return result;}else {System.out.println("验签失败");return "error";}}
}

处理支付宝异步通知的结果

    @Overridepublic String handlePayResult(PayAsyncVo vo) {//1、保存交易流水PaymentInfoEntity infoEntity = new PaymentInfoEntity();infoEntity.setAlipayTradeNo(vo.getTrade_no());infoEntity.setOrderSn(vo.getOut_trade_no());infoEntity.setPaymentStatus(vo.getTrade_status());infoEntity.setCallbackTime(vo.getNotify_time());paymentInfoService.save(infoEntity);//2、修改订单的状态信息if(vo.getTrade_status().equals("TRADE_SUCCESS") || vo.getTrade_status().equals("TRADE_FINISHED")) {//获取订单号String out_trade_no = vo.getOut_trade_no();//根据订单号修改数据库中订单的状态this.baseMapper.updateOrderStatus(out_trade_no, OrderStatusEnum.PAYED.getCode());}return "success";}

application.properties中配置 日期格式化

#日期格式转变
spring.mvc.date-format=yyyy-MM-dd HH:mm:ss

13、收单

我们案例中设置规则是,订单超过1分钟没有下单,就自动过期,数据库改变订单状态为已取消,且已经解锁完库存,但是即使是过了一分钟,依然是可以支付成功的,订单状态变为已付款,如何解决呢?

只要在 AlipayTemplate 类里加了 timeout_express 字段,并且设置了1分钟,下单1分钟后未支付就不能支付了

     //订单相对超时时间String timeout_express = "30m";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\"}");



问题:如果下订单成功后,最后1刻才开始支付,正在支付中,订单就到期库存解锁了

解决:
统一收单交易关闭接口
https://opendocs.alipay.com/open/028wob

支付服务整合支付宝沙箱相关推荐

  1. SpringBoot+Vue整合支付宝沙箱支付

    SpringBoot+Vue2整合实现支付宝沙箱支付 原创不易,转载请注明!!原创不易,转载请注明!!原创不易,转载请注明!!原创不易,转载请注明!!原创不易,转载请注明!! 在进行电脑网站开发时我们 ...

  2. springboot整合支付宝沙箱支付

    springboot整合支付宝沙箱支付 1.简介 支付宝开发平台地址:https://open.alipay.com/develop/sandbox/app 对于学生来说,目前网上确实没有比较统一而且 ...

  3. 基于SpringBoot vue的电脑商城平台源码和论文含支付宝沙箱支付

    演示视频: 基于SpringBoot vue的电脑商城平台源码和论文含支付宝沙箱支付演示视频 支付宝沙箱: package com.java.controller;import java.util.* ...

  4. 支付宝沙箱环境+SpringBoot+内网穿透整合开发

    目录 1.查看沙箱账号 2.内网穿透 3.沙箱环境整合SpringBoot开发 下面我将以实际案例详细介绍如何使用沙箱环境进行支付宝支付对接的开发 1.查看沙箱账号 首先什么是沙箱账号? 沙箱账号是指 ...

  5. 开发笔记 | Springboot整合多平台支付(微信/支付宝)

    目录 微信支付 开发前准备 支付程序编写 (1)创建订单 (2)微信的支付回调 (3)查询订单状态 (4)退款 (5)退款查询与关闭订单 (5)退款回调 alipay-sdk沙箱模拟支付宝支付 官方参 ...

  6. idea支付宝沙箱环境模拟电脑网站支付demo

    1.idea导入demo 支付宝电脑网站支付Demo:https://opendocs.alipay.com/open/270/106291/ 1.导入idea项目 2.选择下载好的项目,选择Ecli ...

  7. 【支付宝沙箱支付】麻瓜教程——申请----代码----修改测试----问题解决

    文章目录 支付宝沙箱环境 沙箱概述: 支付宝沙箱支付操作 `1.搜索进入支付宝开放平台` `2.登录,进入控制台` `3.进入沙箱` `4.进入支付宝开发助手下载所需的工具(度娘搜索就有)` `5.然 ...

  8. SpringBoot整合支付宝付款(沙箱环境)

    由于最近的一个SpringBoot项目中需要整合支付宝付款,所以将过程整理在此,方便查阅.测试前提需要一个支付宝提供的沙箱环境,如果你还没有这种测试账号,可以看姊妹篇获得支付宝沙箱账号 一.引入依赖 ...

  9. Spring Boot 集成支付宝 沙箱支付

    1.沙箱环境简介&基础开发配置 1.进入支付宝官网,扫码进入,网址:https://open.alipay.com/platform/home.htm 2.找到开发服务进入下面的研发服务 3. ...

  10. 一文叫你学会用JavaWeb开发共享图书馆系统(附加支付宝沙箱支付接入)

    前提: 学习书籍: 黑马程序员的<Java Web程序设计任务教程>: 如果没有本文的资源均可以评论留言,我可以免费发给你们学习. 下载安装Eclipse 2021.MySQL 5.5.T ...

最新文章

  1. linux模拟器 cygwin源
  2. C++学习 之 fill和memeset的区别
  3. 微信小程序Java登录流程(ssm实现具体功能和问题解决方案)
  4. sql注入问题-视图-事物-以及存储过程(可视化工具)
  5. 云服务能力评估“国标”出炉,腾讯云TStack首批通过私有云“一级能力”认证
  6. Jmeter中使用循环如何保证数据不重复
  7. 进程的退出方式以及僵尸进程和孤儿进程
  8. 亿佰特物联网dtu无线数传电台:新一代Lora无线模块通信技术
  9. Java 面向对象编程的三大特性——封装、继承、多态
  10. mlock家族:锁定物理内存
  11. python 变量只用一次_Python变量72般变化,只需掌握4点,就可万变不离其宗
  12. Ubuntu之更新CMake的版本
  13. oracle管理表空间和数据文件(笔记)
  14. 2016西安教师职称计算机考试,2016教师职称计算机考试模块.doc
  15. 南开大学c语言100题,计算机二级C语言上机(南开大学)100题.doc
  16. 高德地图打开卫星地图(高德地图设置方法)
  17. 配置Pod的liveness和readiness探针
  18. CSS3弹性布局、响应式布局、PS
  19. 启明创投邝子平谈禾赛上市:做硬科技领域长线投资人
  20. 前大灯是近光灯还是远光灯_前照灯和近光灯一样吗?前照灯就是近光灯吗

热门文章

  1. Collecting package metadata (current_repodata.json)解决方法
  2. Waiting for Jenkins to finish collecting data
  3. 如何批量压缩图片?教你一键批量压缩图片的方法技巧
  4. NWR,Gossip,Paxos分布式一致性协议
  5. 数据去重-----VBA字典法
  6. 你的接口真的线程安全了么?聊聊保证线程安全的10个小技巧
  7. iOS第三方支付集成-支付宝支付
  8. react函数式组件传值之子传父
  9. Vue 数组/对象赋值,视图不更新问题
  10. 布置工作五步法,让工作布置跟高效