当前位置: 首页 > article >正文

支付宝沙箱支付

支付宝沙箱支付

支付宝沙箱(Alipay Sandbox)是支付宝提供的一个模拟环境,用于开发者在不影响真实交易的情况下进行支付宝相关功能的测试和调试。在软件开发中,沙箱环境通常指的是一个隔离的测试环境,可以模拟真实环境的行为,但不会对真实数据产生影响。

支付宝沙箱为开发者提供了一套模拟的支付流程和接口,使开发者能够在沙箱环境中进行支付测试,验证其支付功能是否正常工作,而无需使用真实的资金进行交易。这有助于开发者在应用上线之前进行充分的测试,确保支付流程的稳定性和安全性。

使用支付宝沙箱,开发者可以模拟各种支付场景,包括扫码支付、APP支付、H5支付等。此外,支付宝沙箱还提供了一些调试工具和接口文档,帮助开发者更方便地进行测试和开发。

使用支付宝沙箱环境可以帮助开发者在开发和测试阶段及时发现和解决问题,确保支付功能的稳定和安全。一旦开发者在支付宝沙箱环境中完成了测试和调试,就可以将代码和配置切换到真实的支付宝环境中进行生产部署。

支付宝开放平台 (alipay.com)

准备工作

引入依赖

        <!--支付宝-->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.38.133.ALL</version>
        </dependency>
        <!--其他-->
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.33</version>
        </dependency>

配置文件

alipay:
    appId: # 应用id
    appPrivateKey: # 应用私钥
    alipayPublicKey: # 支付宝公钥
    notifyUrl: # 异步通知地址 
    gateway: # 支付宝网关
server:
    port: 8088

异步通知地址需要能在公网访问:内外穿透教程
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码编写

配置类

AliPayConfig 支付宝支付配置类 方便我们获取配置参数

@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
    private String appId;
    private String appPrivateKey;
    private String alipayPublicKey;
    private String notifyUrl;
    private String gateway;
}

AlipayClientConfig 支付宝客户端配置类

@Configuration
public class AlipayClientConfig {
    @Autowired
    private AliPayConfig aliPayConfig;

    @Bean
    public AlipayClient getAlipayClient(){
        AlipayClient alipayClient = new DefaultAlipayClient(
                aliPayConfig.getGateway(),
                aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(),
                AlipayConstants.FORMAT_JSON,
                AlipayConstants.CHARSET_UTF8,
                aliPayConfig.getAlipayPublicKey(),
                AlipayConstants.SIGN_TYPE_RSA2);
        return alipayClient;
    }
}

controller

AlipayController

@RestController
@RequestMapping("/alipay")
public class AlipayController {
    @Autowired
    private AlipayService alipayService;

    /**
     *  支付接口
     * @param order 订单
     * @return
     */
    @PostMapping("/pay")
    public R pay(@RequestBody Order order){
        // 使用时间当订单号
        order.setOrderId(String.valueOf(System.currentTimeMillis()));
        return alipayService.pay(order);
    }

    /**
     *  订单状态通知 异步通知
     * @param request
     * @return
     * @throws AlipayApiException
     */
    @GetMapping("/notify")
    public R payNotify(HttpServletRequest request) throws AlipayApiException {
        return alipayService.payNotify(request);
    }
}

Service

AlipayService

public interface AlipayService {
    /**
     *  支付接口
     * @param order
     * @return
     */
    R pay(Order order);

    /**
     *  异步通知
     * @param request
     * @return
     */
    R payNotify(HttpServletRequest request);
}

AlipayServiceImpl

@Service
public class AlipayServiceImpl implements AlipayService {
    @Value("${alipay.alipayPublicKey}")
    private String publicKey;
    @Value("${alipay.notifyUrl}")
    private String notifyUrl;

    @Autowired
    private AlipayClient alipayClient;
    @Override
    public R pay(Order order){
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        // 异步通知的地址
        request.setNotifyUrl(notifyUrl);
        Map<String,String> map = new HashMap<>();
        map.put("out_trade_no",order.getOrderId());
        map.put("total_amount",order.getPrice());
        map.put("subject",order.getSubject());
        map.put("body",order.getBody());
        map.put("product_code","FAST_INSTANT_TRADE_PAY");

        // 设置业务参数
        request.setBizContent(JSONObject.toJSONString(map));

        // 发起支付请求
        // 发起支付请求
        AlipayTradePagePayResponse response = null;
        try {
            response = alipayClient.pageExecute(request);
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        // 获取响应结果
        if (response.isSuccess()) {
            System.out.println("调用成功");
            System.out.println("支付宝支付链接:" + response.getBody());
            return R.ok(response.getBody());
        } else {
            System.out.println("调用失败");
            System.out.println("错误信息:" + response.getMsg());
            return R.fail("调用失败",40010);
        }
    }

    @Override
    public R payNotify(HttpServletRequest request) {
        Map<String, String[]> paramMap = request.getParameterMap();
        Map<String, String> params = new HashMap<>();
        for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            StringBuilder valueStr = new StringBuilder();
            for (int i = 0; i < values.length; i++) {
                valueStr.append((i == values.length - 1) ? values[i] : values[i] + ",");
            }
            params.put(name, valueStr.toString());
        }

        // 调用SDK验证签名
        boolean verifyResult = false;
        try {
            verifyResult = AlipaySignature.rsaCheckV1(params, publicKey, AlipayConstants.CHARSET_UTF8, AlipayConstants.SIGN_TYPE_RSA2);
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }

        if (verifyResult) {
            // 验证成功,处理业务逻辑,更新订单状态等
            // 注意:请防止重复处理,可以通过记录支付状态或者订单号来判断是否已经处理过
            System.out.println("支付宝异步通知验证成功");
            // 返回给支付宝成功处理的响应
            return R.ok();
        } else {
            // 验证失败,不处理业务逻辑
            System.out.println("支付宝异步通知验证失败");
            // 返回给支付宝失败处理的响应
            return R.fail("支付失败",40010);
        }
    }
}

工具类

Order 订单类

@Data
public class Order {
    // 订单id
    private String orderId;
    // 价格
    private String price;
    // 商品名称
    private String subject;
    // 商品描述
    private String body;
    // 支付场景
    /**
     * FAST_INSTANT_TRADE_PAY(即时到账):适用于即时交易场景,买家付款后,卖家立即收到款项。
     * QUICK_MSECURITY_PAY(手机网页支付):适用于手机网页支付场景。
     * FACE_TO_FACE_PAYMENT(当面付):适用于线下面对面付款场景,比如扫码支付。
     * APP支付(APP支付场景):适用于在APP内的支付场景。
     * WAP支付(手机网站支付场景):适用于手机网站支付场景。
     * PRE_AUTH(预授权):适用于预先授权场景,买家授权预先冻结资金,商家在完成业务后调用支付宝解冻资金
     */
    private String code;
}

R 响应类

@Data
public class R {
    private String data;
    private String msg;
    private int code;

    public R() {
    }

    public R (String msg, int code) {
        this.msg = msg;
        this.code = code;
    }

    public R( String msg, int code,String data) {
        this.data = data;
        this.msg = msg;
        this.code = code;
    }

    public static R ok(){
        return new R("成功",200);
    }
    public static R ok(String data){
        return new R("成功",200,data);
    }
    public static R fail(String message, int code) {
        return new R( message,code);
    }
}

测试页面

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>商城</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="box">
    <div>
        <h1>订单列表</h1>
        <ul>
            <li v-for="order in orders" :key="order.orderId">
                <h3>{{ order.subject }}</h3>
                <p>价格: ¥{{ order.price }}</p>
                <p>{{ order.body }}</p>
                <button v-on:click="pay(order)">购买</button>
                <hr>
            </li>
        </ul>
    </div>
    <div v-html="paymentFormHtml"></div>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: '#box',
        data: {
            orders: [
                {
                    "price": "12148.00",
                    "subject": "iPhone 15 Pro Max 512GB 原色钛金属",
                    "body": "iPhone 15 Pro Max 512GB 原色钛金属"
                },
                {
                    "price": "4999",
                    "subject": "xiaomi 14 钛金属",
                    "body": "xiaomi 14 钛金属 256gb"
                }
            ],


            paymentFormHtml: "" // 新增 paymentFormHtml 属性
        },
        methods: {
            pay: function (order) {

                axios.post('http://127.0.0.1:8088/alipay/pay', order)

                    .then(res => {
                        // 处理支付成功的逻辑
                        console.log('支付成功', res.data);
                        this.paymentFormHtml = res.data.data;
                        // 手动提交表单

                        // 在延时后提交表单
                        setTimeout(function () {
                            document.forms['punchout_form'].submit();
                        }, 1000); // 1000毫秒,即1秒

                    })
                    .catch(error => {
                        // 处理支付失败或其他错误的逻辑
                        console.error('支付失败', error);
                    });
            }
        }
    });
</script>


</body>

</html>

测试

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


http://www.kler.cn/a/135879.html

相关文章:

  • Qwen2 系列大型语言模型
  • 【循环神经网络】
  • vue3+vite搭建脚手架项目本地运行electron桌面应用
  • Sam Altman:年底将有重磅更新,但不是GPT-5!
  • 【SpringBoot】18 上传文件到数据库(Thymeleaf + MySQL)
  • Android中Activity启动的模式
  • Unity中Shader矩阵的逆矩阵
  • openfeign、nacos获取接口提供方真实IP
  • new/delete 和malloc/free的区别
  • uni-app 使用vscode开发uni-app
  • 接口自动化和UI自动化的区别
  • 实现CAS自旋锁
  • 工程项目立项需要做哪些准备?
  • 视频转码方法:多种格式视频批量转FLV视频的技巧
  • 【Linux网络】详解使用http和ftp搭建yum仓库,以及yum网络源优化
  • git常用命令和参数有哪些?【git看这一篇就够了】
  • 【开题报告】基于SpringBoot的网上摄影工作室的设计与实现
  • 前端面试考核点【更持续新中】
  • 根据nginx日志统计页面访问次数
  • 指针变量和地址
  • 11.1 文件拷贝移动与删除
  • 【Java】异常处理(一)
  • K8S基础笔记
  • 极域电子教室-教师机无法找到学生机
  • ArcEngine:如何进行缩放图层、属性信息显示、状态栏显示?
  • 关于这个“这是B站目前讲的最好的【Transformer实战】教程!“视频的目前可以运行的源代码GPU版本