支付宝支付-当面付之扫码支付「扫码支付」

前言

支付宝支付—沙箱环境使用
支付宝支付-支付宝PC端扫码支付
支付宝支付-手机浏览器H5支付
支付宝支付-当面付之扫码支付「本文

当面付包含两种支付方式:商品条形码支付 + 扫码支付

经过前面两篇PC端扫码支付手机H5支付,我们可以看到一个共同的特点就是接口返回的都是一个Form表单,然后交给提交执行,然后调起支付。其中PC端提交Form表单后跳转至新的窗口进行支付,而手机端H5支付则是唤起支付宝APP支付。

但是现在有这么个场景,我不希望在PC端支付时跳转支付宝网页支付,而是直接将二维码嵌入到系统当中,用户直观看到的就是一个弹窗二维码,然后用户拿支付宝扫码支付…这就是本文的当面付之扫码支付了。

废话不多说,直接进入主题。

本文开发环境:IDEA + Tomcat8.5 + 支付宝沙箱环境 + SpringBoot

补充:调用沙箱环境接口,需要安装沙箱环境下的支付宝APP,不了解的小伙伴可以参考上方 支付宝支付—沙箱环境使用。

一、pom引入依赖 + 参数准备

引入支付宝支付依赖文件,本文基于4.9.153.ALL版本,及供参考, 最新版本可去官方文档查阅。

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

关于支付宝公钥、私钥、回调地址啥的就不再重复了,不清楚的去看支付宝支付—沙箱环境使用。

配置可以单独创建一个类,静态初始化参数::AlipayConfig.java

public class AlipayConfig {
    /** 商户appid **/
    public static String APPID = "201610170070";
    /** 私钥 pkcs8格式的 **/
    public static String RSA_PRIVATE_KEY = "";
    /** 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 **/
    public static String notify_url = "http://ngrok.sscai.club/alipay_trade_wap_pay_java_utf_8_war_exploded/notify_url.jsp";
    /** 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址 **/
    public static String return_url = "http://ngrok.sscai.club/alipay_trade_wap_pay_java_utf_8_war_exploded/return_url.jsp";
    /** 请求网关地址 **/
    public static String URL = "https://openapi.alipaydev.com/gateway.do";
    /** 编码 **/
    public static String CHARSET = "UTF-8";
    /** 返回格式 **/
    public static String FORMAT = "json";
    /** 支付宝公钥 **/
    public static String ALIPAY_PUBLIC_KEY = "";
    /** 日志记录目录 **/
    public static String log_path = "/log";
    /** RSA2 **/
    public static String SIGNTYPE = "RSA2";
    /** 商户门店编号「需要申请当面付」 **/
    public static String STORE_ID = "122211242";
}

几个主要的参数:

  1. APPID :商户appid
  2. RSA_PRIVATE_KEY:应用私钥
  3. ALIPAY_PUBLIC_KEY:支付宝公钥「注意不是应用公钥」

这几个参数不清楚的,可以看一下 沙箱环境使用,或者看一下官方文档参数说明。

二、后端代码

还是之前的代码,一个controller接口方法,一个具体的实现方法「没有使用开源SDK」:

Controller

@ApiOperation(value = "获取支付宝支付二维码")
public Result<AlipayResponse> getAliPayQrCode(@CurrentUser UserModel user, @RequestBody AlipayOrderRequest alipayOrderRequest ) {
    /** 返回给前端的二维码内容 **/
    String sHtmlText = null;
    try {
        /** 判断是否已经存在订单,根据自己业务的不同自行判断**/
        if(!StringUtils.isNotBlank(alipayOrderRequest.getWidOutTradeNo())){
            Orders orders=ordersService.createOrder(user,alipayOrderRequest.getWidTotalFee(),alipayOrderRequest.getAppType());
            alipayOrderRequest.setWidOutTradeNo(orders.getOrderId());
        }else{
            OrderResponse orders=ordersService.searchOrderDetail(user,alipayOrderRequest.getWidOutTradeNo());
            alipayOrderRequest.setWidTotalFee(orders.getAmount().toString());
        }
        /** 这是主要的方法 **/
        sHtmlText = alipayService.alipayOrder(alipayOrderRequest);
    } catch (AlipayApiException e) {
        e.printStackTrace();
    }
    AlipayResponse alipayResponse = new AlipayResponse();
    alipayResponse.setSHtmlText(sHtmlText);
    alipayResponse.setOrderId(alipayOrderRequest.getWidOutTradeNo());
    return ResultUtil.success(alipayResponse);
}

ServerImpl

@Transactional
public String alipayOrder(AlipayOrderRequest alipayOrderRequest) throws AlipayApiException {

    /**获得初始化的AlipayClient**/
    AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl,
            AlipayConfig.app_id,
            AlipayConfig.merchant_private_key,
            "json",
            AlipayConfig.charset,
            AlipayConfig.alipay_public_key,
            AlipayConfig.sign_type);

    /**当面付之「扫码支付」**/
    AlipayTradePrecreateRequest alipayTradePrecreateRequest = new AlipayTradePrecreateRequest();
    /**回调地址+异步通知**/
    alipayTradePrecreateRequest.setNotifyUrl(AlipayConfig.notify_url);
    alipayTradePrecreateRequest.setReturnUrl(AlipayConfig.return_url);

    /**商户订单号,商户网站订单系统中唯一订单号,必填**/
    String out_trade_no = alipayOrderRequest.getWidOutTradeNo();
    /**付款金额,必填**/
    String total_amount = alipayOrderRequest.getWidTotalFee();
    /**订单名称,必填**/
    String subject = alipayOrderRequest.getWidSubject();
    /**商户门店编号,必填**/
    String store_id = AlipayConfig.STORE_ID;
    /**交易超时时间,订单允许的最晚付款时间,必填**/
    String timeout_express = "120m";

    /**拼接参数**/
    alipayTradePrecreateRequest.setBizContent(
        "{\"out_trade_no\":\""+ out_trade_no +"\","
        + "\"total_amount\":\""+ total_amount +"\","
        + "\"subject\":\""+ subject +"\","
        + "\"store_id\":\""+ store_id +"\","
        + "\"timeout_express\":\""+timeout_express+"\"}");

    return alipayClient.execute(alipayTradePrecreateRequest).getBody();
}

三、返回结果+测试

如上代码调用execute之后的返回结果:

{
    "alipay_trade_precreate_response":{
        "code":"10000",
        "msg":"Success",
        "out_trade_no":"20200508160037744742",
        "qr_code":"https://qr.alipay.com/bax06173nktjyrrwupss00bf"
    },
    "sign":"sdk3Q6idlQC+SRuuxc6xXv3g4BAkxEgpA9WYJoiE8oYH5mA6K8+GMwAETNKOhOPh/SoYS4CECzswk/H7qw9A=="
}

我们可以看到qr_code参数了,剩下的就是交给前端生成二维码了,然后用户扫码支付,在这我就不展示了,直接拿链接去草料二维码直接生成个二维码试试。

四、扫码支付回调地址

支付成功后支付宝会回调前边设置的回调地址,由后端的的 return_url 参数控制。

再看看支付成功后的回调接口,由后端的 notify_url 参数控制「没有使用开源的SDK演示」:

public String alipaynotify(Model model, HttpServletRequest request{

    log.info("支付宝异步回调 ------------beg-----------");
    String result = "fail";
    /**获取支付宝POST过来反馈信息**/
    /* *
     * 功能:支付宝服务器异步通知页面
     * 说明:
     * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
     * 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
     */

    Map<String, String> params=this.getAlipayRequest(request);
    if(params == null || params.size()==0){
        BufferedReader bufferReader = null;
        StringBuilder sb = new StringBuilder();
        try {
            bufferReader = new BufferedReader(request.getReader());

            String line = null;
            while ((line = bufferReader.readLine()) != null) {
                sb.append(new String(line.getBytes("ISO-8859-1"), "utf-8"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String body= null;
        try {
            body = URLDecoder.decode(sb.toString(),"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        params=UriComponentsBuilder.newInstance().query(body).build().getQueryParams().toSingleValueMap();
    }
    boolean signVerified =false;
    try {
        signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
    } catch (AlipayApiException e1) {
        log.error("由于"+e1.getErrMsg()+"返回给支付宝系统的结果result:fail");
        model.addAttribute("result""fail");
        return result;
    }

    /**——请在这里编写您的程序(以下代码仅作参考)——**/

    /* 实际验证过程建议商户务必添加以下校验:
    1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
    2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
    3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
    4、验证app_id是否为该商户本身。
    */

    log.error("支付宝验证签名:---------------------------------"+signVerified);
    if(signVerified) {/**验证成功**/
        /**商户订单号**/
        /**交易状态**/
        log.info("支付宝异步回调验签成功!");
        String trade_status = params.get("trade_status");

        if("TRADE_FINISHED".equals(trade_status)){
            /**判断该笔订单是否在商户网站中已经做过处理**/
            /**如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序**/
            /**如果有做过处理,不执行商户的业务程序**/

            /**注意:**/
            /**退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知**/
            try {
                /** 在这里处理支付成功后的操作,比如修改订单状态等等**/
                coding...
                result = "success";
            } catch (Exception e) {
                log.error(e.getMessage());
                result = "fail";
            }
        }else if ("TRADE_SUCCESS".equals(trade_status)){
            /**判断该笔订单是否在商户网站中已经做过处理**/
            /**如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序**/
            /**如果有做过处理,不执行商户的业务程序**/

            /**注意:**/
            /**付款完成后,支付宝系统发送该交易状态通知**/
            try {
                /** 在这里处理支付成功后的操作,比如修改订单状态等等**/
                coding...
                result = "success";
            } catch (Exception e) {
                log.error(e.getMessage());
                result = "fail";
            }
        }else{
            result = "fail";
        }
    }else {/**验证失败**/
        result = "fail";
        /**调试用,写文本函数记录程序运行情况是否正常**/
        /**String sWord = AlipaySignature.getSignCheckContentV1(params);**/
        /**AlipayConfig.logResult(sWord);**/
        log.debug("支付宝异步回调验签失败");
    }
    log.debug("异步回调返回给支付宝系统的结果result:"+result);

    model.addAttribute("result", result);
    log.info("支付宝异步回调  -------------end ------------");
    return result;
}

该方法返回给支付宝的 result 就 successfail 两个结果。
从以上看来,其实不难发现支付宝支付是非常简单的,尽管我上边贴了大量的代码,其实采用开源SDK的话可以更加缩减、美化一些。

ok,这篇文章就到这结束了,上边并没有详细介绍接口调用、参数说明等,详细介绍请查看官方文档:

https://opendocs.alipay.com/open/194/106078/
https://opendocs.alipay.com/open/194/103296

文章最后

博客地址:https://www.cgblog.com/niceyoo

如果觉得这篇文章有丶东西,不妨关注一下我,关注是对我最大的鼓励~

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

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

相关文章

MybatisCodeHelperNew-2.8.1-191-201插件使用

本文测试环境IDEA_2020.1&#xff0c;文中提供了MacOS用户操作截图 1、文件解压后放置plugs插件目录「Windows」 扫码回复「139」 下载后将文件解压&#xff0c;将压缩包内的 MyBatisCodeHelper-Pro 放入 IDEA 安装目录的 plugins 目录。 2、如果你是MacOS用户「MacOS」 同样找…

Redis简单案例(四) Session的管理

Redis简单案例(四) Session的管理 原文:Redis简单案例(四) Session的管理负载均衡&#xff0c;这应该是一个永恒的话题&#xff0c;也是一个十分重要的话题。毕竟当网站成长到一定程度&#xff0c;访问量自然也是会跟着增长&#xff0c;这个时候&#xff0c; 一般都会对其进行负…

MacOS中Elasticsearch的安装「借助Homebrew」

1、Homebrew 如果你已经安装过Homebrew了&#xff0c;那么你可以跳过这一步&#xff0c;直接进行Elasticsearch安装步骤&#xff1b; Homebrew是一款MacOS平台下的软件包管理工具&#xff0c;拥有安装、卸载、更新、查看、搜索等很多实用的功能&#xff0c;强烈推荐安装。 请…

负载均衡中使用 Redis 实现共享 Session

最近在研究Web架构方面的知识&#xff0c;包括数据库读写分离&#xff0c;Redis缓存和队列&#xff0c;集群&#xff0c;以及负载均衡&#xff08;LVS&#xff09;&#xff0c;今天就来先学习下我在负载均衡中遇到的问题&#xff0c;那就是session共享的问题。 一、负载均衡 负…

Typora中使用Gitee图床

1、前言 之前好友写了一篇「使用gitee作为图床 ,写markdown自动上传文件」&#xff0c;初衷是由于我一直使用的是Typora来写博客「力推」&#xff0c;但之前的版本都不支持图床功能&#xff0c;现在新版本已经有了图床功能了&#xff0c;赶紧入坑。 本篇环境&#xff1a;MacOS…

【NOIP2017模拟6.25】小W的动漫

题目 小W最近迷上了日本动漫&#xff0c;每天都有无数部动漫的更新等着他去看&#xff0c;所以他必须将所有的动漫排个顺序&#xff0c;当然&#xff0c;虽然有无数部动漫&#xff0c;但除了1号动漫&#xff0c;每部动漫都有且仅有一部动漫是它的前传&#xff08;父亲&#xff…

用Elasticsearch代替数据库存储日志方式

之前的项目中一直使用的是数据库表记录用户操作日志的&#xff0c;但随着时间的推移&#xff0c;数据库log单表是越来越大「不考虑删除」&#xff0c;再加上近期项目中需要用到Elasticsearch&#xff0c;所以干脆把这些用户日志迁移到ES上来了。 环境&#xff1a;SpringBoot2.2…

[js] 写一个方法实现promise失败后自动重试

[js] 写一个方法实现promise失败后自动重试 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&…

如何理解Java中的自动拆箱和自动装箱?

小伟刚毕业时面的第一家公司就被面试官给问住了&#xff0c;记忆尤深啊… 如何理解Java中的自动拆箱和自动装箱&#xff1f; 自动拆箱&#xff1f;自动装箱&#xff1f;什么鬼&#xff0c;听都没听过啊&#xff0c;这…这…知识盲区… 回到家后小伟赶紧查资料&#xff0c;我…

基于Docker的Redis集群简单搭建

环境&#xff1a;Docker ( Redis:5.0.5 * 3 ) 1、拉取镜像 docker pull redis:5.0.52、创建Redis容器 创建三个 redis 容器&#xff1a; redis-node1&#xff1a;6379redis-node2&#xff1a;6380redis-node3&#xff1a;6381 docker create --name redis-node1 -v /data…

Python 全栈开发十 socket网络编程

一、客户端&#xff08;client&#xff09;服务端&#xff08;sever&#xff09;架构 在计算机中有很多常见的C/S架构&#xff0c;例如我们的浏览器是客户端、而百度网站和其他的网站就是服务端&#xff1b;视频软件是客户端&#xff0c;提供视频的腾讯、优酷、爱奇艺就是服务端…

基于Docker方式实现Elasticsearch集群

文本环境&#xff1a;Docker (Elasticsearch6.8.5 * 3) 1、拉取Elasticsearch docker pull elasticsearch6.8.52、创建es挂载目录 创建3个文件夹用于存放es挂载地址&#xff1a;es01、es02、es03 [rootCentOS7 ~]# mkdir /es-cluster [rootCentOS7 ~]# cd /es-cluster/ [ro…

基于Docker搭建Gitlab代码存储

关于Docker搭建Gitlab&#xff0c;在19年时就已经在博客发过文章了&#xff0c;今天重新回顾一下。 1、拉取镜像 docker pull gitlab/gitlab-ce默认拉取最新版本&#xff1a; 2、创建Gitlab配置 创建GitLab 的配置 (etc) 、 日志 (log) 、数据 (data) 放到容器之外&#xff…

读书笔记--Android Gradle权威指南(上)

本篇文章已授权微信公众号 dasu_Android&#xff08;大苏&#xff09;独家发布 最近看了一本书《Android Gradle 权威指南》&#xff0c;对于 Gradle 理解又更深了&#xff0c;但不想过段时间就又忘光了&#xff0c;所以打算写一篇读书笔记&#xff0c;将书中一些我个人觉得蛮有…

基于Docker搭建私有镜像仓库

通常我们在docker中拉取的镜像都是在docker hub在线存储库中获取的&#xff0c;这个在线存储库里的docker镜像可以由任何用户发布和使用&#xff0c;显然这在某些场景下是不适用的&#xff0c;比如某些互金的隐私项目&#xff0c;或者是公司完全处于内网状态不能访问外网&#…

volatile理解了吗?

到这里大家感觉自己对volatile理解了吗&#xff1f; 如果理解了&#xff0c;大家考虑这么一个问题&#xff1a;ReentrantLock&#xff08;或者其它基于AQS实现的锁&#xff09;是如何保证代码段中变量&#xff08;变量主要是指共享变量&#xff0c;存在竞争问题的变量&…

Linux|CentOS下配置Maven环境

1、下载maven包 wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz如果提示 wget: 未找到命令&#xff0c;请尝试如下指令安装 wget yum -y install wget2、解压下载的maven压缩吧 tar -xzvf apache-maven-3.3.…

CentOS中安装Docker步骤

1、安装仓库所需要的软件包 yum install -y yum-utils device-mapper-persistent-data lvm22、设置yum加速源 yum-config-manager --add-repo http://mirrors.aliyun.com/repo/Centos-7.repo3、安装docker-ce yum install docker-ce docker-ce-cli containerd.io4、启动dock…

Docker+Jenkins+Git+GitLab实现DevOps

先了解一下Jenkins Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具&#xff0c;提供了数百个插件来支持构建&#xff0c;部署和自动化任何项目。我们可以使用Jenkins结合常用的版本控制工具(git、svn等)来实现自动部署项目&#xff0c;比如说我们从本地上传代码到G…

毕业两年的大专生程序员工作总结(java后端)

文章目录前言这一年做了啥去年的学习清单今年的学习清单第三年的规划最后唠叨的话前言 如题&#xff0c;这是我毕业第二年的工作总结&#xff0c;对第一年工作总结感兴趣的请戳这《毕业一年的大专生程序员工作总结》&#xff0c;再简单介绍一下我以及这个系列的文章。 关于我…