沙箱环境下实现支付宝网站支付

  • 前言
  • 前期准备
  • 1.沙箱环境配置
  • 2.创建SpringBoot项目,导入相关依赖
  • 3.创建沙箱环境配置文件
  • 4.商品列表页面编写
  • 5. 编写checked.html
  • 7. 根据返回的pay编写pay.html页面
  • 8.补充

前言

由于非商户号不能接入微信支付与支付宝支付的功能、同时第三方支付平台等收费的原因,使得个人学习者学习的同时又要交上真金白银。但第三方支付平台存在的不稳定因素(?),使得此次实验选择的环境是支付宝的沙箱环境。学习接入支付功能的话,沙箱环境足够用以学习。但是沙箱环境毕竟是虚拟的,如果你想真正实现业务,完成实际上的支付功能,那么上面所说的商户号与第三方支付平台,才是你的选择。

前期准备

  1. 沙箱环境配置 :具体参考如何使用沙箱环境

1.沙箱环境配置

如果你完成了上述所说的沙箱环境配置以后,那么可以进入沙箱应用,查看自己的具体配置内容。


确认无误后,便可以开始进行开发。

2.创建SpringBoot项目,导入相关依赖


此处因节省开发时间,没有选择使用数据库,你可以根据自己需要进行选择。(使用Lombok时IDE需要安装Lombok插件)
同时在pom.xml导入支付宝支付SDK

<!-- 支付宝支付SDK -->
<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.10.90.ALL</version>
</dependency>

至此,项目创建完成

3.创建沙箱环境配置文件

resources目录下创建aliPayConfig.properties 文件,填入下列信息:

#以下六个信息可以从沙箱环境中获取,参考上述中1.沙箱环境配置
appid=沙箱环境中的appid
privateKey=沙箱环境中的应用私钥(理应保存在本地目录中,自寻查找)
publicKey=沙箱环境中的支付宝公钥(注意,不是应用公钥)
gatewayUrl=https://openapi.alipaydev.com/gateway.do
signType=RSA2(默认为:RSA2,根据公钥生成时选择的策略自行修改)
charset=UTF-8(默认为:UTF-8)#同步通知与异步通知,具体配置可看下文(目录位置:7.1)
returnUrl=http://host:port/alipayReturnNotice
notifyUrl=http://host:port/alipayNotifyNotice

同时,可写一个类,用于读取该配置信息

@Component
@PropertySource(value = "classpath:alipayConfig.properties") //读取指定配置文件
public class PayConfig {//appIdpublic static String app_id;//应用私钥public static String merchant_private_key;//支付宝公钥public static String alipay_public_key;//支付宝服务器主动通知商户服务器里指定的页面http/https路径(异步通知请求)public static String notify_url;//HTTP/HTTPS开头字符串(同步返回请求)public static String return_url;//商户生成签名字符串所使用的签名算法类型public static String sign_type;//请求使用的编码格式public static String charset;//网关链接public static String gatewayUrl;@Value("${appid}")public void setApp_id(String app_id) {PayConfig.app_id = app_id;}@Value("${privateKey}")public void setMerchant_private_key(String merchant_private_key) {PayConfig.merchant_private_key = merchant_private_key;}@Value("${publicKey}")public void setAlipay_public_key(String alipay_public_key) {PayConfig.alipay_public_key = alipay_public_key;}@Value("${notifyUrl}")public void setNotify_url(String notify_url) {PayConfig.notify_url = notify_url;}@Value("${returnUrl}")public void setReturn_url(String return_url) {PayConfig.return_url = return_url;}@Value("${signType}")public void setSign_type(String sign_type) {PayConfig.sign_type = sign_type;}@Value("${charset}")public void setCharset(String charset) {PayConfig.charset = charset;}@Value("${gatewayUrl}")public void setGatewayUrl(String gatewayUrl) {PayConfig.gatewayUrl = gatewayUrl;}
}

至此,沙箱环境就在项目中配置好。

4.商品列表页面编写

此处本应该从数据库中读取商品列表,再经Controller传到前端页面。为节省开发时间,直接编写静态页面内容。编写页面之前,可以选择引入Bootstrap框架,美化前端页面。

如果你想了解更多的关于Bootstrap的使用方法,可以点击此处。

  1. product.html
<!doctype html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><link th:href="@{/customer/css/bootstrap.css}" rel="stylesheet"><title>商品列表页面</title></head>
<body><div class="container"><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12"><table class="table table-striped"><thead><tr><th scope="col">产品编号</th><th scope="col">产品名称</th><th scope="col">产品价格</th><th scope="col">操作</th></tr></thead><tbody><tr><th scope="row">1001</th><td>苹果</td><td>0.01</td><td><a th:href="@{/checked(productId=1001)}">购买</a></td></tr><tr><th scope="row">1002</th><td>香蕉</td><td>0.02</td><td><a th:href="@{/checked(productId=1002)}">购买</a></td></tr></tbody></table></div></div>
</div><script th:src="@{/customer/js/jquery-3.4.0.js}"></script>
<script th:src="@{/customer/js/popper.min.js}"></script>
<script th:src="@{/customer/js/bootstrap.min.js}"></script>
</body></html>
  1. 编写完商品列表页面后,接着写一个PayController.java进行映射
@Controller
public class PayController {/*** 跳往商品列表页面* @return*/@GetMapping("/to")public String toProduct() {return "product";}
}
  1. product.html中的按钮需要进行映射,继续编写PayController.java
 /*** 获取商品详情信息 跳往确认订单页面* @param productId 商品id* @param map* @return*/@RequestMapping(value = "/checked")public Object goChecked(String productId, Map<String, Object> map) {//TODO 根据id获取产品 此处静态绑定 根据具体业务自行修改if (productId.equals("1001")) {map.put("id", "1001");map.put("name", "苹果");map.put("price", "0.01");} else {map.put("id", "1002");map.put("name", "香蕉");map.put("price", "0.02");}return "checked";}

5. 编写checked.html

<!doctype html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><link th:href="@{/customer/css/bootstrap.css}" rel="stylesheet"><title>购物车</title>
</head>
<body><div class="container"><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12"><form method="post"><input type="hidden" id="productId" name="productId" value="${id}"/><div class="form-group"><label for="id">产品编号</label><input type="text" class="form-control" id="id" th:value="${id}" readonly></div><div class="form-group"><label for="name">产品名称</label><input type="text" class="form-control" id="name" th:value="${name}" readonly></div><div class="form-group"><label for="price">产品价格</label><input type="text" class="form-control" id="price" th:value="${price}" readonly></div><div class="form-group"><label for="buyCounts">购买个数</label><input type="text" class="form-control" id="buyCounts" name="buyCounts"></div><input class="btn btn-primary" type="button" value="确认" onclick="toPay()"/></form></div></div>
</div><script th:src="@{/customer/js/jquery-3.4.0.js}"></script>
<script th:src="@{/customer/js/popper.min.js}"></script>
<script th:src="@{/customer/js/bootstrap.min.js}"></script>
<script type="text/javascript">function toPay() {var count = $("#buyCounts").val()if (count==0 || !count) {alert("请输入购买数量")return;}$.ajax({url: "/saveOrder", type: "POST",data: {"productId": $("#productId").val(),"buyCounts": $("#buyCounts").val(),"price": $("#price").val()},dataType: "json",success: function (data) {if (data.status == 200 && data.msg == "OK") {// 保存订单后,进入支付页面window.location.href = "/pay?orderId=" + data.data;} else {alert(data.msg);}}});}
</script>
</body></html>
  1. toPay()事件中,ajax异步提交了一个Json数据,我们可以新建一个Product实体类,对Json数据进行接收
/*** 下单商品实体类* <p>*/
@Data
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Product {//商品idprivate String productId;//商品购买数量private String buyCounts;//商品单价private Double price;
}
  1. 同时,再编写一个通用结果返回类CommonResult,将数据通过Json格式返回给前端
/*** 通用结果返回* <p>*/
@Data
@Getter
@Setter
@NoArgsConstructor
public class CommonResult {// 定义jackson对象private static final ObjectMapper MAPPER = new ObjectMapper();// 响应业务状态private Integer status;// 响应消息private String msg;// 响应中的数据private Object data;private String ok;public CommonResult(Object data) {this.status = 200;this.msg = "OK";this.data = data;}public CommonResult(Integer status, String msg, Object data) {this.status = status;this.msg = msg;this.data = data;}public static CommonResult ok() {return new CommonResult(null);}public static CommonResult ok(Object data) {return new CommonResult(data);}
}
  1. 对toPay()中的ajax实现映射,继续编写PayController.java
 /*** 保存订单* @param order* @return* @throws Exception*/@ResponseBody@RequestMapping(value = "/saveOrder")public CommonResult saveOrder(Product product) throws Exception {//TODO 根据product生成订单,此处直接返回,不做具体业务return CommonResult.ok("orderid");}
  1. 上面 saveOrder方法中,直接返回ok给前端,返回后ajax进行成功函数的回调

  2. 根据判断编写/pay映射,继续编写PayController.java

 /*** 跳往支付页面** @param orderId* @return* @throws Exception*/@RequestMapping(value = "/pay")public Object goPay(String orderId, Map<String, Object> map) throws Exception {//TODO 根据orderId实现业务,此处静态绑定,你根据具体业务自行修改map.put("orderId", UUID.randomUUID().toString().replace("-", ""));map.put("pName","苹果");map.put("orderAmount","0.02");map.put("buyCounts","2");return "pay";}

7. 根据返回的pay编写pay.html页面

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link th:href="@{/customer/css/bootstrap.css}" rel="stylesheet"><title>确认订单</title>
</head>
<body><div class="container"><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12"><form id="payForm" th:action="@{/aliPay}" method="post"><input type="hidden" name="orderId" th:value="${orderId}"/><div class="form-group"><label for="orderId">订单编号</label><input type="text" class="form-control" id="orderId" th:value="${orderId}" readonly></div><div class="form-group"><label for="pName">产品名称</label><input type="text" class="form-control" id="pName" th:value="${pName}" readonly></div><div class="form-group"><label for="orderAmount">订单价格</label><input type="text" class="form-control" id="orderAmount" th:value="${orderAmount}" readonly></div><div class="form-group"><label for="buyCounts">购买个数</label><input type="text" class="form-control" id="buyCounts" th:value="${buyCounts}" readonly></div><input class="btn btn-primary" type="submit" value="支付宝支付"></form></div></div>
</div><script th:src="@{/customer/js/jquery-3.4.0.js}"></script>
<script th:src="@{/customer/js/popper.min.js}"></script>
<script th:src="@{/customer/js/bootstrap.min.js}"></script>
</body>
</html>
  1. 表单提交到/aliPay,继续编写PayController.java,进行映射
 /*** 前往支付宝第三方网关进行支付* @param orderId* @return* @throws Exception*/@ResponseBody@RequestMapping(value = "/aliPay", produces = "text/html; charset=UTF-8")public String goAlipay(String orderId) {//接口调用配置初始化AlipayClient alipayClient = new DefaultAlipayClient(PayConfig.gatewayUrl, PayConfig.app_id, PayConfig.merchant_private_key, "json", PayConfig.charset, PayConfig.alipay_public_key, PayConfig.sign_type);//AlipayTradePagePayRequest,调用的网站支付接口//沙箱环境有多种支付接口可供选择,但请求参数不同,具体参考如下://电脑网站支付:https://opendocs.alipay.com/open/270/105899///其他支付:https://opendocs.alipay.com/open/009ypxAlipayTradePagePayRequest request = new AlipayTradePagePayRequest();//通知配置参考:https://opensupport.alipay.com/support/helpcenter/193/201602472201?ant_source=manual&recommend=ab2418594aa12994227b51f38a16d735//异步通知request.setReturnUrl(PayConfig.return_url);//同步通知request.setNotifyUrl(PayConfig.notify_url);/** 请求参数配置* 参考API:https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay#?scene=API002020081300013629请求参数*///TODO 此处根据需要进行业务处理,为节省事件则静态绑定数据String out_trade_no = orderno;String total_amount = "0.02";String subject = "苹果";String body = "用户订购商品个数:2个";request.setBizContent("{" +"\"out_trade_no\":\"" + out_trade_no + "\"," +"\"total_amount\":" + total_amount + "," +"\"subject\":\"" + subject + "\"," +"\"body\":\"" + body + "\"" +"}");try {//执行请求,返回body数据return alipayClient.pageExecute(request).getBody();} catch (AlipayApiException e) {return e.getMessage();}}

8.补充

至此,沙箱调用支付宝的网站支付功能就此实现,对于后续支付成功后需要跳转到自定义的成功页面,在代码中,我们使用了request.setReturnUrl(PayConfig.return_url); 这里,我的配置为:http://localhost:8087/alipayReturnNotice

注意:该地方使用的同步返回,如果是前后端分离的项目
(1)如果在配置中填写的是前端页面完整的访问地址,那么会在付款后自动跳转回目标地址的前端页面。
(2)如果在配置中填写的是后端业务操作的访问地址,那么会在付款后自动跳转回后端,进行相对应的业务操作。
ps:在(2)中对业务操作完成后,如想返回前端页面,可以通过重定向返回到前端。

  1. 继续编写PayController.java,对支付成功后的回调进行处理:
 /*** 网页重定向通知* 支付宝同步返回页面GET(买家付款完成以后进行自动跳转)*/@RequestMapping(value = "/alipayReturnNotice")public Object alipayReturnNotice(HttpServletRequest request, HttpServletRequest response) throws Exception {// 获取支付宝以GET方式提交的反馈信息Map<String, String> params = new HashMap();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.rsaCheckV2(params, PayConfig.alipay_public_key, PayConfig.charset, PayConfig.sign_type);//设置返回视图ModelAndView mv = new ModelAndView("success");// TODO 验签成功后,进行相对应的业务处理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");mv.addObject("out_trade_no", out_trade_no);mv.addObject("trade_no", trade_no);mv.addObject("total_amount", total_amount);mv.addObject("productName", "苹果");} else {//TODO 验签失败业务处理}return mv;}
  1. success.html页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<title>支付成功页面</title>
<link th:href="@{/customer/css/bootstrap.css}" rel="stylesheet">
<head>
</head><body>
<div class="container"><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12">&nbsp;</div></div><div class="row"><div class="col-md-12"><table class="table table-striped"><thead><tr><th scope="col">订单编号</th><th scope="col">支付宝交易号</th><th scope="col">实付金额</th><th scope="col">购买产品</th></tr></thead><tbody><tr><th scope="row" th:text="${out_trade_no}"></th><th scope="row" th:text="${trade_no}"></th><th scope="row" th:text="${total_amount}"></th><th scope="row" th:text="${productName}"></th></tr></tbody></table></div></div>
</div><script th:src="@{/customer/js/jquery-3.4.0.js}"></script>
<script th:src="@{/customer/js/popper.min.js}"></script>
<script th:src="@{/customer/js/bootstrap.min.js}"></script>
</body></html>

自此,项目编写完成 ,进行测试。



注意,此处需要用沙箱测试应用(Android),并使用沙箱买家账号进行扫码,才可完成支付。电脑或者没有该安卓应用的,可以使用右侧的登录付款,用沙箱买家账号进行登录后付款。上述所说的应用及账号,可在沙箱环境中找到。



参考项目:tiankong0310/springboot-weixin-alipay
参考资料:

  1. 沙箱环境
  2. 电脑网站支付
  3. 统一收单下单并支付页面接口

沙箱环境下实现支付宝网站支付相关推荐

  1. 支付宝沙箱环境下支付接口的错误

    背景 笔者使用alipay-sdk-PHP-4.9.1调试沙箱环境下的支付宝支付接口时,发现同步通知的验签总是失败,按照官方说明的检查清单做了排查,仍然不能解决. 进入官方的钉钉圈子,咨询后,也没有解 ...

  2. 支付宝沙箱环境下模拟下单流程

    目前,市面上比较流行的支付方式有支付宝支付,微信支付,银联支付,其他聚合支付等,支付市场发展到目前,算是比较稳定和成熟了,有了这么多支付方式,不论是web端还是移动端,需要支付的场景很多,今天来简单模 ...

  3. 对接支付宝网站支付接口

    今天因为业务需要线上支付充值,所以需要对接支付宝的网站支付接口.首先去支付宝开发者中心看了一遍demo:网址如下:https://docs.open.alipay.com/270/106291/ 大致 ...

  4. Springboot整支付宝网站支付、APP支付、单笔转账给用户、退款功能

    一.概述 最近在写项目,遇到了要调用支付宝支付的地方(后台管理页面的网站支付,APP内的用户支付,APP内用户提现也就是单笔转账给用户.退款).研究了好久,全部调通了,下面把我的代码分享出来. 首先那 ...

  5. java支付宝网站支付

    想要做支付宝网站支付,首先必须要有企业级商户号,并且成功签约,得到PID和key controller就简单的写一下,异步回调就不写了,页面在后面 @RequestMapping(value = &q ...

  6. SEO学习笔记二:在搜索引擎竞价排名环境下,个人网站将何去何从?

    本文首发于「妙蛙种子前端」博客,欢迎关注- 早期的搜索引擎,大家都在一个相对公平的规则内玩耍:你的内容够好,网站体验更优秀,在搜索引擎中的排名一般都会比较高. 因为搜索引擎能便捷的为我们定位到精准的内 ...

  7. 如何连接ipv6服务器_ipv4网络环境下测试ipv6网站 ipv6proxy.cn

    本文讲述如何在IPv4环境下测试IPv6网站内容 测试一个网站是否支持ipv6访问,有很多在线测试网址,比如下面几个,在网站页面输入框中输入你想要查询的域名即可. http://ipv6-test.c ...

  8. [go]沙盒环境下调用支付宝扫码支付

    参考于这篇博客,在此基础上进行了封装 配置支付宝开放平台 支付宝开放平台,使用支付宝扫码并成为开发者.然后进入沙盒进行测试 下载沙盒版支付宝并使用沙盒账号中的买家信息进行登陆,之后使用此账号登陆的支付 ...

  9. JAVA-Servlet项目接入支付宝网站支付

    支付宝电脑支付流程,Servlet版 原文地址:https://www.cnblogs.com/it-taosir/p/9728374.html 感谢原博主,大佬无视即可,工作一年多第一次写博客,不足 ...

最新文章

  1. pandas使用query函数删除dataframe中两个数据列加和小于某一特定值的数据行(removing rows based on multiple dataframe column value
  2. .net版 类似火车头的网页采集
  3. linux 档案类型s,Linux学习(四)档案与目录管理
  4. redis五种数据类型的应用场景_Redis五种不同的数据类型
  5. Dubbo支持的协议
  6. 社会计算机比赛,哈尔滨工业大学社会计算与信息检索研究中心 – 理解语言,认知社会 » IR-Lab参加计算机学院“光熙杯”篮球赛...
  7. 判断点是否在给定四边形内的算法
  8. apache目录 vscode_CentOS 上使用vscode 调试百度大数据分析框架Apache Doris BE
  9. 基于主特征空间相似度计算的切分算法及切分框架
  10. json与字面量定义对象的区别,请不要混淆,与字符串转换
  11. 物流前沿理论与方法1
  12. matlab lbp特征,lbp特征(lbp纹理特征提取)
  13. 用手机玩转ContextCapture(Smart3D)跑出惊艳三维模型
  14. 角度和弧度之间的转换
  15. mysql字符集异常_mysql字符集设置出错问题
  16. 360浏览器如何拦截java_安全卫士安装了JAVA,但是还是被浏览器拦截。
  17. 阿里云 (ECS 部署Javaweb 以及虚拟机操作)
  18. python如何返回一个列表_python如何返回元组,列表或字典的?
  19. 解决JDBC中Parameter number X is not an OUT parameter
  20. 论文翻译解读:PARIS :Probabilistic Alignment of Relations, Instances, and Schema

热门文章

  1. C语言-输出最长单词
  2. 数据结构童话版 003新的学生
  3. _EPROCESS结构简单了解!
  4. 评论:雨林木风Linux 意义和目的是什么?
  5. MAC OS ssh key永久保存
  6. SQL注入题型(手工注入+sqlmap)
  7. 按键精灵-UI配置丢失问题解决办法
  8. elasticsearch实践之代码结构设计,java银行项目面试题
  9. 基于ResNet50网络的简单垃圾分类网络
  10. 汇编语言: txt文件操作