Java-支付宝(PC扫码支付,退款)从0-1

一.准备工作

登录支付宝开放平台进行扫码登录
登录后对个人信息填写在这里插入图片描述
右上角进入管理中心
在这里插入图片描述
选择研发服务
在这里插入图片描述
进入界面对密钥进行设置
在这里插入图片描述

下载工具

下载密钥生成工具
在这里插入图片描述
手机下载支付宝沙箱版,用于测试时付款
在这里插入图片描述

开发思想

来自官网
在这里插入图片描述
调用顺序如下:

  • 商户系统请求支付宝接口 alipay.trade.page.pay,支付宝对商户请求参数进行校验,而后重新定向至用户登录页面。
  • 用户确认支付后,支付宝通过 get 请求 returnUrl(商户入参传入),返回同步返回参数。
  • 交易成功后,支付宝通过 post 请求 notifyUrl(商户入参传入),返回异步通知参数。
  • 若由于网络等问题异步通知没有到达,商户可自行调用交易查询接口 alipay.trade.query
    进行查询,根据查询接口获取交易以及支付信息(商户也可以直接调用查询接口,不需要依赖异步通知)。

注意:

  • 由于同步返回的不可靠性,支付结果必须以异步通知或查询接口返回为准,不能依赖同步跳转。

  • 商户系统接收到异步通知以后,必须通过验签(验证通知中的 sign 参数)来确保支付通知是由支付宝发送的。详细验签规则参考异步通知验签。

  • 接收到异步通知并验签通过后,一定要检查通知内容,包括通知中的 app_id、out_trade_no、total_amount
    是否与请求中的一致,并根据 trade_status 进行后续业务处理。

  • 在支付宝端,partnerId 与 out_trade_no 唯一对应一笔单据,商户端保证不同次支付 out_trade_no
    不可重复;若重复,支付宝会关联到原单据,基本信息一致的情况下会以原单据为准进行支付

二. Maven项目创建

注意:该案例只是做一个简单的测试不连接数据库

项目结构

在这里插入图片描述

导入pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jiang</groupId>
    <artifactId>alipay</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>alipay</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入thymeleaf的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.9.124.ALL</version>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.22</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件

application.yml

server:
  port: 8080

spring:
  profiles:
    active: dev

application-dev.yml

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML5
    encoding: UTF-8
    servlet:
      content-type: text/html
    cache: false

配置类的编写

应用ID(APPID):在准备工作时,信息配置中获取,每个用户的不一样
商户私钥(merchant_private_key ):在使用密钥生成工具时,会得到
支付宝网关(gatewayUrl ):沙箱环境下的网关与正式开发的网关有所区别
界面跳转(notify_url | return_url ):在支付成功之后,跳转到指定界面,因为要外网能访问的到,所以我们可以下载一个网络穿透工具

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://48bfwd.natappfree.cc/Pay/notify_url";

    // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    public static String return_url = "http://48bfwd.natappfree.cc/Pay/return_url";

    // 签名方式
    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";
	
	//初始化设置
    public AlipayClient getClient() {
        AlipayClient alipayClient = new DefaultAlipayClient
                (AlipayConfig.gatewayUrl,
                        AlipayConfig.app_id,
                        AlipayConfig.merchant_private_key,
                        AlipayConfig.format,
                        AlipayConfig.charset,
                        AlipayConfig.alipay_public_key,
                        AlipayConfig.sign_type);
        return alipayClient;
    }
}

网络穿透工具natapp配置

1.natapp的下载

并且在该网站上注册
购买一个免费隧道
在这里插入图片描述
在我的隧道中查看authtoken,将其复制

在这里插入图片描述

2.natapp的配置

在natapp目录下创建config.ini文件

[default]
authtoken=在此处添加隧道的authtoken
clienttoken=
log=none
loglevel=ERROR
http_proxy=

保存,退出,打开nataapp.exe

接口编写

public interface AlipayService {
    /**
     * 网页扫码支付
     * @param amount 金额
     * @param trade_no 订单号
     * @return
     */
    AlipayTradePagePayResponse PCPay(String amount,String trade_no) throws AlipayApiException;

    /**
     * 退款
     * @param refund_amount 退款金额,退款时要对传入金额进行验证,不可以大于商品的原价
     * @param trade_no 订单号
     * @return
     * @throws AlipayApiException
     */
    AlipayTradeRefundResponse Refund(String refund_amount, String trade_no) throws AlipayApiException;
}

接口实现

@Service
public class AlipayServiceImpl implements AlipayService {


    //当天未支付,订单超时
    private String timeout_express = "1h";

    private AlipayClient alipayClient = new AlipayConfig().getClient();


    /**
     * 电脑网站支付(PC支付)
     * @param total_amount 订单金额
     * @param trade_no 商家订单号
     * @param subject 订单标题
     * @return
     * @throws AlipayApiException
     */
    @Override
    public AlipayTradePagePayResponse PCPay(String total_amount,String trade_no) throws AlipayApiException {


        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        //设置支付成功跳转界面
        alipayRequest.setReturnUrl(AlipayConfig.return_url);
        alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
		
		//因为subject是必填参数,为了简单起见,将其固定
        String subject = "test";

		// product_code设置的是实时到账方式,当前支付宝仅支持这种方式
        alipayRequest.setBizContent("{\"out_trade_no\":\""+ trade_no +"\","
                + "\"total_amount\":\""+ total_amount +"\","
                + "\"subject\":\""+ subject +"\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        //请求
        AlipayTradePagePayResponse result = alipayClient.pageExecute(alipayRequest);

        return result;
    }


    @Override
    public AlipayTradeRefundResponse Refund(String refund_amount, String trade_no) throws AlipayApiException {
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        String id = trade_no +"1";
        request.setBizContent("{" +
                "\"out_trade_no\":\""+trade_no+"\"," +
                "\"refund_amount\":\""+refund_amount+"\"," +
                "\"refund_reason\":\"正常退款\"," +
                "\"out_request_no\":\""+id+"\"}");
        AlipayTradeRefundResponse response = alipayClient.execute(request);
        return response;
    }
}

控制层的编写

案例简单,未对支付成功,退款成功之后的业务进行编写

/**
 * 支付宝支付功能
 */
@Controller
@RequestMapping("/Pay")
public class AlipayController {

    @Autowired
    private AlipayService alipayService;

    /**
     * PC端支付
     * @param total_amount 金额
     * @param trade_no 商品的单号
     * @return 返回一个支付宝二维码界面
     * @throws AlipayApiException
     */

    @RequestMapping(value = "/PCPay", method = RequestMethod.POST)
    @ResponseBody
    public JSONObject PCPay(String total_amount, String trade_no) throws AlipayApiException {
        System.out.println("/PCPay");
        JSONObject jsonObject = new JSONObject();
        AlipayTradePagePayResponse alipayTradePagePayResponse = alipayService.PCPay(total_amount,trade_no);
        jsonObject.put("msg", alipayTradePagePayResponse);
        return jsonObject;
    }

    /**
     * 退款
     * @param refund_amount 退款金额
     * @param trade_no 商品的单号
     * @return
     * @throws AlipayApiException
     */
    @RequestMapping(value = "/Refund", method = RequestMethod.GET)
    @ResponseBody
    public JSONObject Refund(String refund_amount,String trade_no) throws AlipayApiException {
        System.out.println("Refund");
        JSONObject jsonObject = new JSONObject();
        AlipayTradeRefundResponse refund = alipayService.Refund(refund_amount, trade_no);
        jsonObject.put("msg", refund);
        return jsonObject;
    }

    @RequestMapping(value = "/toRefund")
    public String toRefund(){
        System.out.println("toRefund");
        return "thymeleaf/refund";
    }


    @RequestMapping("index")
    public String index(ModelMap map) {
        return "thymeleaf/index";
    }

    /**
     * 异步调用接口
     * @return
     */
    @RequestMapping("notify_url")
    public String notify_url() {
        return "thymeleaf/notify_url";
    }

    /**
     * 同步调用接口
     * @param request
     * @return
     */
    @RequestMapping("return_url")
    public String return_url(HttpServletRequest request) {
        Map<String,String[]> requestParams = request.getParameterMap();
        return "thymeleaf/return_url";
    }
}

thymeleaf模板

index.html
涉及付款功能,退款界面跳转

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div class="amount_total">5</div>
    <button class="pay">付款</button><br/>
    <a href="/Pay/toRefund">想要退款</a>
</body>
<script type="text/javascript" src="/js/jquery-3.5.1.min.js"></script>
<script>
    var trade_no = 2020;
    var amount = $('.amount_total').text();
    init();
    function init() {
        var trade_no_lenth = 8;
        for (var i = 0; i < trade_no_lenth; i++) {
            trade_no += parseInt(Math.random() * 9)+'';
        }
        $('.amount_total').append('<a>商品单号:'+trade_no+'</a>')
    }

    // var amount = document.getElementsByClassName('amount_total')
    $('.pay').on('click',function () {
        console.log(amount)
        console.log(trade_no)
            $.ajax({
                url: "http://localhost:8080/Pay/PCPay",
                type: 'POST',
                dataType: 'json',
                data: ({
                    "total_amount": amount,
                    "trade_no": trade_no
                }),
                success: function (msg) {
                    console.log(msg)
                    console.log(msg.msg.body)
                    $('.pay').append(msg.msg.body)
                },
                error: function (e) {
                    console.log(e)
                }
            })
    });

</script>
</html>

refund.html
付款功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    单号:<input class="trade_no"/>
    退款金额:<input class="refund_amount"/>
    <button class="refund">退款</button>
</body>
<script type="text/javascript" src="/js/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
    $('.refund').on('click', function () {
        var refund_amount = $('.refund_amount').val()
        var trade_no = $('.trade_no').val()
        console.log(refund_amount,trade_no)
        $.ajax({
            url: "http://localhost:8080/Pay/Refund",
            type: 'GET',
            dataType: 'json',
            data: ({
                "refund_amount": refund_amount,
                "trade_no": trade_no
            }),
            success: function (msg) {
                console.log(msg)
            },
            error: function (e) {
                console.log(e)
            }
        })
    })
</script>
</html>

其他功能的实现

参考官网API

三.功能展示

在这里插入图片描述

付款功能

在这里插入图片描述
在这里插入图片描述

退款功能

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值