spring boot:集成支付宝(沙箱环境)(spring boot 2.4.0 / wap/h5方式 )

news2024/11/18 16:22:08

一,配置支付宝沙箱环境: 

1,沙箱的地址:

登录 - 支付宝

也可以登录后,从控制台点击 研发服务 进入

2,下载开发助手:并生成密钥

从这个页面,按自己所在的平台下载,

当前支持 windows,macos

开发助手简介 | 网页&移动应用

下载完成后安装

运行支付宝开发助手:点击 生成密钥 按钮,

3,把公钥保存到支付宝后台的沙箱环境

后台->沙箱应用->RSA2(SHA256)密钥(推荐)

点击:设置:

加签模式:选择公钥:

复制公钥后,点 保存设置 按钮

保存生成的 应用公钥 和 支付宝公钥,后面要使用到

4,下载安装支付宝沙箱钱包,目前只有android版

用支付宝扫描后下载到手机,安装

可以在这个页面,选择 用浏览器打开   然后下载完后安装

下载成功后安装,运行app:

注意登录时用支付宝后台沙箱环境的买家账号登录:

说明:刘宏缔的架构森林是一个专注架构的博客,地址:刘宏缔的架构森林 - 博客园

         对应的源码可以访问这里获取: liuhongdi (刘宏缔) · GitHub

说明:作者:刘宏缔 邮箱: 371125307@qq.com

  

二,使用官方sdk

1,服务端支付宝库的maven配置地址:

官方文档地址:

概述 | 网页&移动应用

从这里可以看到官方提供的sdk库的地址:

https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java

建议大家使用官方的sdk,

因为更新有保障 

三,java代码

1,结构:

2,pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.10.192.ALL</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!--thymeleaf begin-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

3,application.properties

#error
server.error.include-stacktrace=always
#error
logging.level.org.springframework.web=trace

#thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

#port
server.port=9090

4,AliPayConfig.java

@Component
public class AliPayConfig {

    // 应用ID,APPID,开发时是沙箱提供的APPID,生产环境是公司的APPID
    public static String APP_ID = "202100118860153";

    // 商户私钥,之前所生成的密钥中的私钥
    public static String APP_PRIVATE_KEY = "JIP3TV5YNWizgZCuP3";

    // 支付宝公钥,注意是APPID下的支付宝公钥,不是应用的公钥
    public static String ALIPAY_PUBLIC_KEY = "MIIBIjADAQAB";

    // 服务器异步通知页面路径,需要用http://格式的完整路径,不要加自定义参数,需要外网可以正常访问
    public static String notify_url = "http://alipay.paydemo.net/alipay/notify";

    // 同步通知页面跳转路径 需要用http://格式的完整路径,不要加自定义参数,用来显示支付成功后返回的页面
    public static String return_url = "http://alipay.paydemo.net/alipay/return";

    // 签名方式
    public static String sign_type = "RSA2";

    // 字符编码格式
    public static String CHARSET = "utf-8";

    // 支付宝网关,我们这里用沙箱的网关,生产环境中要替换成正式环境的网关
    public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";

    // 返回格式
    public static String FORMAT = "json";

}

5,PayController.java

@Controller
@RequestMapping("/alipay")
public class PayController {

    //wap:QUICK_WAP_WAY
    //web:FAST_INSTANT_TRADE_PAY
    private static final String PRODUCT_CODE = "QUICK_WAP_WAY";

    //表单页面
    @GetMapping("/home")
    public String index(ModelMap modelMap) {
        return "home/home";
    }

    //调起支付
    @RequestMapping("/pay")
    @ResponseBody
    public void pay(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 商户订单号,商户网站订单系统中唯一订单号,必填
        String out_trade_no = request.getParameter("out_trade_no");
        // 付款金额,必填
        String total_amount = request.getParameter("total_amount");
        // 订单名称,必填
        String subject = request.getParameter("subject");
        // 商品描述,可空
        String body = request.getParameter("body");

        AlipayClient client = new DefaultAlipayClient(AliPayConfig.gatewayUrl, AliPayConfig.APP_ID, AliPayConfig.APP_PRIVATE_KEY, AliPayConfig.FORMAT, AliPayConfig.CHARSET, AliPayConfig.ALIPAY_PUBLIC_KEY,AliPayConfig.sign_type);
        AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();

        String timeout_express="2m";

        // 封装请求支付信息
        AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
        model.setOutTradeNo(out_trade_no);
        model.setSubject(subject);
        model.setTotalAmount(total_amount);
        model.setBody(body);
        model.setTimeoutExpress(timeout_express);
        model.setProductCode(PRODUCT_CODE);
        alipay_request.setBizModel(model);
        // 设置异步通知地址
        alipay_request.setNotifyUrl(AliPayConfig.notify_url);
        // 设置同步地址
        alipay_request.setReturnUrl(AliPayConfig.return_url);

        // form表单生成
        String form = "";
        try {
            // 调用SDK生成表单
            form = client.pageExecute(alipay_request).getBody();

            System.out.println("form:");
            System.out.println(form);

            response.setContentType("text/html;charset=" + AliPayConfig.CHARSET);
            response.getWriter().write(form);//将表单html写到页面
            response.getWriter().flush();
            response.getWriter().close();
        } catch (AlipayApiException e) {

            e.printStackTrace();
        }
    }

    //支付完成后的返回
    @RequestMapping("/return")
    @ResponseBody
    public String returnCall(HttpServletRequest request, HttpSession session, Model model) throws Exception {
        // 获取支付宝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] + ",";
            }
            params.put(name, valueStr);
        }
        System.out.println("params");
        System.out.println(params);
        System.out.println("\n验签开始.....\n");

        boolean signVerified = AlipaySignature.rsaCheckV1(params, AliPayConfig.ALIPAY_PUBLIC_KEY, AliPayConfig.CHARSET, AliPayConfig.sign_type); //调用SDK验证签名
        if (signVerified) {
            System.out.println("return sign  success");
            return "验证签名成功,现在跳转到订单详情页面";
        } else {
            System.out.println("return sign  failed");
            return "验证签名失败";
        }
    }

/*
*
*
* TRADE_SUCCESS状态代表了充值成功,也就是说钱已经进了支付宝(担保交易)或卖家(即时到账);
* 这时候,这笔交易应该还可以进行后续的操作(比如三个月后交易状态自动变成TRADE_FINISHED),
* 因为整笔交易还没有关闭掉,也就是说一定还有主动通知过来。
* 而TRADE_FINISHED代表了这笔订单彻底完成了,不会再有任何主动通知过来了。

综上所述,收到TRADE_FINISHED请求后,这笔订单就结束了,支付宝不会再主动请求商户网站了;
* 收到TRADE_SUCCESS请求后,后续一定还有至少一条通知记录,即TRADE_FINISHED。
* 所以,在做通知接口时,切记使用判断订单状态用或的关系
*
*
* */

    //异步通知
    @RequestMapping("/notify")
    @ResponseBody
    public String notifyCall(HttpServletRequest request, HttpSession session, Model model) throws Exception {
        // 获取支付宝反馈信息
        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] + ",";
            }
            params.put(name, valueStr);
        }
        System.out.println("params:");
        System.out.println(params);

        String tradeStatus = params.get("trade_status");
        System.out.println("tradeStatus:");
        System.out.println(tradeStatus);

        System.out.println("\n验签开始.....\n");
        boolean signVerified = AlipaySignature.rsaCheckV1(params, AliPayConfig.ALIPAY_PUBLIC_KEY, AliPayConfig.CHARSET, AliPayConfig.sign_type); //调用SDK验证签名

        if (signVerified) {
            System.out.println("notify sign  success");
            /*
            if(trade_status.equals("TRADE_FINISHED")){
            } else if (trade_status.equals("TRADE_SUCCESS")){
            }
             */
            return "success";
        } else {
            System.out.println("notify sign  failed");
            return "fail";
        }
    }
}

6,home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
    <meta name="viewport" content="width=640, user-scalable=no">
</head>
<body style="width:640px;margin:0px;font-size: 24px;">
<div style="width:calc(100vw - 20px); margin-left: 10px;margin-top: 10px;">
<form action="/alipay/pay" method="post">
    订单编号:<input style="width:400px;font-size: 22px;" type="text" name="out_trade_no" required><br/>
    订单名称:<input style="width:400px;font-size: 22px;" type="text" name="subject" required><br/>
    付款金额:<input style="width:400px;font-size: 22px;" type="text" name="total_amount" required><br/>
    b o d y:<input style="width:400px;font-size: 22px;" type="text" name="body"><br/>
    <input style="width:150px;height:30px;margin-top: 10px;" type="submit" value="下单">
    <input style="width:150px;height:30px;margin-top: 10px;" type="reset" value="重置">
</form>
</div>
</body>
</html>

四,配置nginx,及运行demo

1,nginx的配置文件 

[root@blog ~]# more /usr/local/openresty/nginx/conf/conf.d/alipay.conf
upstream alipay {
      server  127.0.0.1:9090 weight=10;
}
server {
    listen       80;
    server_name  alipay.paydemo.net;
    index         index.html;
    location /{
                proxy_pass http://alipay/;
    }
    access_log      /data/logs/nginxlogs/alipay_web.access_log;
    error_log       /data/logs/nginxlogs/alipay_web.error_log;
}

2,jar包的运行脚本 

[root@blog ~]# more /data/alipay/tools/startalipay.sh
#!/bin/bash
export BUILD_ID=dontKillme
whoami
WORKSPACE=/data/alipay/jar
JDK_PATH=/usr/local/soft/jdk-15/bin/java
JAR_NAME=demo-0.0.1-SNAPSHOT.jar

#echo ${JAR_NAME}
#echo "ps -ef | grep ${JAR_NAME} | grep -v grep | awk '{print \$2}'";
PID=`ps -ef | grep ${JAR_NAME} | grep -v grep | grep -v startup | awk '{print \$2}'`
echo $PID;
if [ ! "$PID" ] ;then
     echo "进程不存在"
else
    echo "进程存在,杀死进程PID$PID"
    kill -9 $PID
fi

echo "服务启动开始"
nohup ${JDK_PATH} -jar ${WORKSPACE}/${JAR_NAME} >/data/alipay/logs/run.log 2>&1 &

五,测试效果:

1,访问/alipay/home地址

跳转到支付页面,如图:

选择  使用支付宝app付款

在弹出选择两个支付宝app时,选择 沙箱版,

注意密码是沙箱后台给出的买家的支付密码

支付成功后点 已完成付款:

页面返回:

2,查看运行日志:

notify回调记录的日志

params:
{gmt_create=2020-12-08 11:29:49, charset=utf-8, seller_email=mesays4070@sandbox.com, subject=德国进口不绣钢旅行杯4, 
sign=D/9wd/EmZ9xPZVhyx/reTiFGqOB2sgL4KKj2G6ta8mJT2GhAw18+Iuuog3rNwfeGHfaF679cMczTKxUQlJD9JnAsGmt8j7FsgDYhlk/CIKmTB79lj
7kouDavDCB0bxjDA+mqBu3oVOZCXH0qoRYA0dq+4pWJPTosMUsYMXHD9Q5UCS6Q3REOakTFGnYwvWs5+xf3CBSW1uBZUsa17vsw==, 
body=德国进口不绣钢旅行杯4, buyer_id=20885145241, invoice_amount=35.00, notify_id=202012222112945240513810363, 
fund_bill_list=[{"amount":"35.00","fundChannel":"ALIPAYACCOUNT"}], notify_type=trade_status_sync, trade_status=TRADE_SUCCESS, 
receipt_amount=35.00, buyer_pay_amount=35.00, app_id=2021000660153, sign_type=RSA2, seller_id=208862735781, 
gmt_payment=2020-12-08 11:29:49, notify_time=2020-12-08 11:29:50, version=1.0, out_trade_no=2020131301, 
total_amount=35.00, trade_no=2020120822001445240514181897, auth_app_id=20210060153, 
buyer_logon_id=nxq***@sandbox.com, point_amount=0.00}
tradeStatus:
TRADE_SUCCESS
 
验签开始.....
 
notify sign  success

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/125201.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自定义Tango Control设备服务在Ubuntu中的测试

文章目录环境create a device classcompile the device classregister the devicestart the deviceexplore the device故障问题解决参考继续上一篇&#xff1a;https://blog.csdn.net/woshigaowei5146/article/details/128443892?spm1001.2014.3001.5501 环境 虚拟机&#xf…

布隆过滤器 以及缓存穿透问题

1. 前言 今天从客观的角度来分析下&#xff0c;什么是缓存穿透&#xff0c;什么是布隆过滤器&#xff0c;布隆过滤器是如何解决缓存穿透的。 2. 适合人群 对布隆过滤器不熟悉的人对Redis 缓存穿透不熟悉的人 3. 开始 3.1 什么是缓存穿透 其实通过上图我们可以知道&#xff0…

(七)Filter

Filter 表示过滤器&#xff0c;是JavaWeb 三大组件(Servlet、Filter、Listener)之一。过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;权限控制、统一编码处理、敏感字符处理等等..一、Filte…

彻底理解Redis持久化

前言 大家都知道Redis一个内存数据库,它支持2种持久化方式&#xff1a;RDB(Snapshot 内存快照) &#xff0c;AOF(append only file)。持久化功能将内存中的数据同步到磁盘来避免Redis发生异常导致数据丢失的情况。当Redis实例重启时&#xff0c;即可利用之前持久化的文件实现数…

软件项目管理教程

软件项目管理 1. 软件项目管理 1.1 概述 概念 项目是为了创造一个唯一的产品或提供一个唯一的服务而进行的临时性的努力 软件项目特征 目标性 相关性 周期性 独特性 没有完全一样的项目”&#xff0c;项目的这种独特性对实际项目管理有非常重要的指导意义&#xff0c;因此…

跨境电商日本市场再创纪录,亚马逊失去流量第一位置

从跨境电商服务行业市场分析人员拿到的近期的各平台的销售数据和相关工作总结来看&#xff0c;美国市场微热&#xff0c;而反观欧洲市场则有些遇冷。 此外&#xff0c;近几年的疫情及各国相关政策并未影响到跨境电商市场的整体大好趋势&#xff0c;而伴随着疫情逐渐被控制&…

认真学习MySQL中的MVCC机制

什么是MVCC&#xff1f;MVCC&#xff08;Multiversion Concurrency Control&#xff09;&#xff0c;多版本并发控制。顾名思义&#xff0c;MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性操作有了保证。换言之&#…

Aspose.PDF for Java系列1-使用前说明

一、关于pdf文件说明 什么是pdf文件&#xff1f; PDF是Portable Document Format的缩写&#xff0c;用来以电子形式显示文档&#xff0c;不受软件、硬件或者操作系统的影响。 最初是由Adobe开发&#xff0c;是一种基于PostScript格式的通用兼容文件格式&#xff0c;现在已成为…

当云原生网关遇上图数据库,NebulaGraph 的 APISIX 最佳实践

本文介绍了利用开源 API 网关 APISIX 加速 NebulaGraph 多个场景的落地最佳实践&#xff1a;负载均衡、暴露接口结构与 TLS Termination。 API 网关介绍 什么是 API 网关 API 网关是位于客户端和服务器之间的“中间人”&#xff0c;用于管理、监控和保护 API。它可以在 API 之…

GAMES101 现代计算机图形学入门

Lecture1 Overview of CG 计算机图形学概述Lecture2 Review of Linear Algebra 向量与线性代数Lecture3 2D Transformation 基础变换&#xff08;二维&#xff09;3.1 线性变换 (寻找变换前后直角坐标坐标关系)3.2齐次坐标 Homogeneous Coordinate3.3 仿射变换Affine Transform…

SpringBoot2核心技术(核心功能)- 05、Web开发【5.4 数据响应与内容协商】

4、数据响应与内容协商 1、响应JSON 1.1、jackson.jarResponseBody <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> web场景自动引入了json场景<dependenc…

在黑金zynqmp 上用emmc 启动的那些坑

缘由&#xff1a;由于硬件设计可能存在问题&#xff0c;导致sd 开启动失败&#xff0c;高速的时候&#xff0c;在建立VFS 时就会引起kernel 崩溃&#xff0c;官方的板子就正常&#xff0c;自己的板子第一版硬件可以&#xff0c;第二版就不行 思路&#xff1a;从emmc 启动 替代…

Hadoop 的基础知识

Hadoop 的基础知识1. Hadoop 简介2. Hadoop 的发展简史3. Hadoop 现状4. Hadoop 特性优点5. Hadoop 发行版本6. Hadoop 架构变迁7. Hadoop 集群集体概念1. Hadoop 简介 Hadoop 官网: https://hadoop.apache.org/ Apache Hadoop 软件库是一个框架, 是 Apache 软件基金会的一款开…

IoT 物联网将如何影响 SCADA ?

IoT 物联网将如何影响 SCADA &#xff1f;-钡铼技术 随着工业物联网&#xff08;IIOT&#xff09;或工业4.0的提出&#xff0c;未来工业自动化将是大势所趋&#xff0c;机器设备运行自动化&#xff0c;人不断被机器替代。那么&#xff0c;这些发展方向会对SCADA有什么影响&…

【DevOps:一、开始】

名词安全组类似于防火墙的设置&#xff0c;打开安全组&#xff0c;但要防止被当成矿机&#xff0c;需要设置主机的容器密码VPC专有网络&#xff0c;私有网络&#xff1a;VPC虽然网段相同&#xff1a;但资源物理层隔离&#xff0c;不能使用内网相互通Ipv4网段子网计数器容器平台…

产品设计表现技能的学习要点

在产品设计过程中&#xff0c;当你心中有创意设计时&#xff0c;你需要写下创意设计&#xff0c;并生成一个例子来总结你以前的想法。此时&#xff0c;您需要设计性能。在设计性能的过程中&#xff0c;我们需要使用各种设计工具&#xff0c;这些设计工具的应用技术和方法通常称…

玩以太坊链上项目的必备技能(内联汇编 [inline assembly]-Solidity之旅十八)

概要 大抵是讲到汇编&#xff0c;身为编程开发者的我们脑瓜子早就嗡嗡作响了。看那晦涩难懂的低级汇编代码&#xff0c;敢断言&#xff0c;那一行不是我写的&#xff0c;其他行也不是我写的。 自从C语言问世&#xff0c;而后类C语言犹如雨后春笋般地搅动着IT界&#xff0c;而…

【金猿人物展】实在智能创始人、CEO孙林君:我们为什么坚定在IPA方向努力?...

‍孙林君本文由实在智能创始人、CEO孙林君撰写并投递参与“数据猿年度金猿策划活动——2022大数据产业趋势人物榜单及奖项”评选。‍数据智能产业创新服务媒体——聚焦数智 改变商业充满“不确定性”的2022年即将过去&#xff0c;不确定性带来的不仅是挑战&#xff0c;还有新机…

安装 Azure CL 并生成 service principal 文件

1 安装 1.1 Yum⽅式安装 For Linux distributions with yum such as RHEL, Fedora, or CentOS, theres a package for the Azure CLI. This package has been tested with RHEL 7, Fedora 19 and higher, and CentOS 7. sudo rpm --import https://packages.microsoft.com/k…

【nowcoder】笔试强训Day11

目录 一、选择题 二、编程题 2.1最近公共祖先 2.2求最大连续bit数 一、选择题 1.下面哪个标识符是合法的&#xff1f; A. 9HelloWorld B. _Hello World C. Hello*World D. Hello$World java标识符的命名规则应以字母、下划线、美元符开头&#xff0c;后跟字母、下划线…