java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java 沙箱支付

java配置沙箱支付的实现示例

作者:hqxstudying

箱支付是支付平台提供的模拟支付环境,包含测试用的 APPID、密钥、网关、测试账号等资源,下面就来介绍一下如何实现,感兴趣的可以了解一下

在 Java 项目中集成沙箱支付(以支付宝、微信支付这两大主流平台为例)是支付功能开发的必经环节,用于在测试环境验证支付流程的正确性,避免真实资金交易风险。以下是详细的集成步骤,包含前期准备、环境搭建、核心功能实现及测试验证。

一、沙箱支付概述

沙箱支付是支付平台(如支付宝、微信支付)提供的模拟支付环境,包含测试用的 APPID、密钥、网关、测试账号等资源,其接口逻辑与正式环境一致,但交易仅为模拟,不涉及真实资金。
核心作用:验证支付下单、支付跳转、异步通知、订单查询等全流程的正确性。

二、支付宝沙箱支付集成步骤(详细)

2.1 前期准备:获取沙箱环境参数

  1. 注册支付宝开放平台账号
    访问 支付宝开放平台,注册开发者账号并完成实名认证(个人开发者即可)。

  2. 创建应用并开通沙箱

    • 进入「控制台」→「开发者中心」→「网页 & 移动应用」,点击「创建应用」(选择 “自研” 类型)。
    • 应用创建后,在应用详情页的「开发设置」中,开启 “沙箱” 功能(沙箱环境默认自动生成,无需审核)。
  3. 获取核心参数
    在「沙箱环境」页面获取以下参数(后续代码中需使用):

    • APPID:沙箱应用唯一标识(如2021000000000000)。
    • 支付宝网关:沙箱环境专用网关 https://openapi.alipaydev.com/gateway.do(正式环境为https://openapi.alipay.com/gateway.do)。
    • 密钥:需生成 RSA2 密钥对(公钥 + 私钥):
      • 下载 支付宝开放平台开发助手,生成 RSA2(2048 位)密钥对。
      • 将生成的应用公钥上传到沙箱环境的「密钥管理」中,支付宝会自动生成对应的支付宝公钥(需保存,用于验签)。
      • 本地保留应用私钥(用于接口签名)。
  4. 获取沙箱测试账号
    在沙箱环境页面可获取测试用的买家账号、买家支付密码、测试银行卡信息(用于模拟支付)。

2.2 环境搭建:引入依赖与配置

  1. 引入支付宝 Java SDK
    在项目的pom.xml(Maven)中添加依赖(最新版本可参考支付宝 SDK 文档):

    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.38.0.ALL</version>
    </dependency>
    
  2. 配置沙箱参数
    application.properties(或application.yml)中配置参数:

    # 支付宝沙箱配置
    alipay.app-id=你的沙箱APPID
    alipay.private-key=你的应用私钥(生成的私钥,不含换行)
    alipay.public-key=支付宝公钥(上传应用公钥后生成的)
    alipay.gateway-url=https://openapi.alipaydev.com/gateway.do
    alipay.format=json
    alipay.charset=UTF-8
    alipay.sign-type=RSA2
    
  3. 创建配置类
    编写 Java 配置类,初始化支付宝客户端(AlipayClient):

    @Configuration
    public class AlipayConfig {
        @Value("${alipay.app-id}")
        private String appId;
        @Value("${alipay.private-key}")
        private String privateKey;
        @Value("${alipay.public-key}")
        private String publicKey;
        @Value("${alipay.gateway-url}")
        private String gatewayUrl;
        @Value("${alipay.format}")
        private String format;
        @Value("${alipay.charset}")
        private String charset;
        @Value("${alipay.sign-type}")
        private String signType;
    
        @Bean
        public AlipayClient alipayClient() {
            // 初始化支付宝客户端(沙箱环境)
            return new DefaultAlipayClient(
                gatewayUrl, appId, privateKey, format, charset, publicKey, signType
            );
        }
    }
    

2.3 核心功能实现

(1)创建支付订单(电脑网站支付为例)

用户下单后,调用支付宝接口生成支付表单,前端跳转至沙箱支付页面。

@Service
public class AlipayService {
    @Autowired
    private AlipayClient alipayClient;

    /**
     * 创建支付宝支付订单
     * @param outTradeNo 商户订单号(唯一)
     * @param totalAmount 订单金额(单位:元)
     * @param subject 订单标题
     * @param returnUrl 支付成功后同步跳转地址(商户系统页面)
     * @param notifyUrl 支付成功后异步通知地址(商户系统接口)
     * @return 支付表单HTML(前端自动提交跳转)
     * @throws AlipayApiException 接口调用异常
     */
    public String createPayOrder(String outTradeNo, String totalAmount, String subject, 
                                 String returnUrl, String notifyUrl) throws AlipayApiException {
        // 1. 创建支付请求对象(电脑网站支付用AlipayTradePagePayRequest)
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        // 设置同步回调地址(用户支付成功后跳转)
        request.setReturnUrl(returnUrl);
        // 设置异步通知地址(支付宝主动推送支付结果)
        request.setNotifyUrl(notifyUrl);

        // 2. 组装请求参数(JSON格式)
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", outTradeNo); // 商户订单号(自定义,需唯一)
        bizContent.put("total_amount", totalAmount); // 订单金额(精确到分)
        bizContent.put("subject", subject); // 订单标题
        bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 产品码(固定值)
        request.setBizContent(bizContent.toString());

        // 3. 调用支付宝接口,获取支付表单
        AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
        if (response.isSuccess()) {
            return response.getBody(); // 返回HTML表单,前端渲染后自动提交
        } else {
            throw new RuntimeException("创建支付订单失败:" + response.getMsg());
        }
    }
}
(2)编写控制器接口

提供给前端调用,触发支付流程:

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

    /**
     * 发起支付宝支付
     */
    @PostMapping("/create")
    public Result<String> createPay(@RequestBody PayDTO payDTO) {
        try {
            // 生成唯一商户订单号(可基于UUID或雪花算法)
            String outTradeNo = "ORDER_" + System.currentTimeMillis();
            // 调用服务生成支付表单
            String payForm = alipayService.createPayOrder(
                outTradeNo, 
                payDTO.getTotalAmount(), // 金额(如"0.01"元)
                payDTO.getSubject(), // 订单标题(如"测试订单")
                "http://localhost:8080/pay/success", // 同步回调地址(前端页面)
                "http://你的服务器公网地址/pay/alipay/notify" // 异步通知地址(需公网可访问)
            );
            return Result.success(payForm);
        } catch (Exception e) {
            return Result.error("支付创建失败:" + e.getMessage());
        }
    }
}
(3)处理异步支付通知(核心!)

支付宝在用户支付成功后,会主动向notifyUrl发送 POST 请求(携带支付结果),商户需验证通知合法性并更新订单状态。

@PostMapping("/notify")
public String handleNotify(HttpServletRequest request) {
    try {
        // 1. 获取请求参数(支付宝通知的所有参数)
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (String key : requestParams.keySet()) {
            String[] values = requestParams.get(key);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(key, valueStr);
        }

        // 2. 验证签名(关键!防止伪造通知)
        boolean signVerified = AlipaySignature.rsaCheckV1(
            params, 
            alipayPublicKey, // 支付宝公钥(配置文件中获取)
            charset, // 编码(UTF-8)
            signType // 签名类型(RSA2)
        );

        if (!signVerified) {
            return "fail"; // 签名验证失败,返回fail(支付宝会重试通知)
        }

        // 3. 验证通知参数(确保支付状态为成功)
        String tradeStatus = params.get("trade_status");
        if (!"TRADE_SUCCESS".equals(tradeStatus)) {
            return "fail"; // 支付未成功,不处理
        }

        // 4. 处理业务逻辑(更新订单状态、记录支付信息等)
        String outTradeNo = params.get("out_trade_no"); // 商户订单号
        String tradeNo = params.get("trade_no"); // 支付宝交易号
        String totalAmount = params.get("total_amount"); // 支付金额

        // 示例:调用订单服务更新状态为“已支付”
        orderService.updateOrderStatus(outTradeNo, tradeNo, totalAmount);

        // 5. 处理完成,返回"success"(支付宝收到后停止重试)
        return "success";
    } catch (Exception e) {
        // 异常时返回fail,支付宝会重试
        return "fail";
    }
}
(4)查询支付状态(可选)

用于主动查询订单支付结果(如用户未同步跳转时):

public String queryPayStatus(String outTradeNo) throws AlipayApiException {
    AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
    JSONObject bizContent = new JSONObject();
    bizContent.put("out_trade_no", outTradeNo); // 商户订单号
    request.setBizContent(bizContent.toString());

    AlipayTradeQueryResponse response = alipayClient.execute(request);
    if (response.isSuccess()) {
        return response.getTradeStatus(); // 返回状态(如TRADE_SUCCESS、WAIT_BUYER_PAY)
    } else {
        throw new RuntimeException("查询支付状态失败:" + response.getMsg());
    }
}

2.4 测试流程

  1. 前端处理:将后端返回的payForm(HTML 表单)渲染到页面,表单会自动提交至支付宝沙箱支付页面。
  2. 模拟支付
    • 使用沙箱环境提供的买家账号登录(非开发者账号)。
    • 输入沙箱买家支付密码(如111111),完成支付。
  3. 验证结果
    • 同步跳转:支付成功后跳转到returnUrl(前端成功页)。
    • 异步通知:支付宝会向notifyUrl发送通知,验证订单状态是否更新为 “已支付”。
    • 主动查询:调用queryPayStatus接口,确认状态为TRADE_SUCCESS

三、微信支付沙箱支付集成步骤(简要)

微信支付沙箱流程与支付宝类似,但细节略有不同(如需要证书、沙箱密钥需动态获取)。

3.1 前期准备

  1. 注册 微信支付商户平台,获取商户号(mch_id)
  2. 申请 API 证书(在商户平台的「账户中心→API 安全」中下载,包含apiclient_cert.p12等文件)。
  3. 生成 API 密钥(32 位,用于签名),并在商户平台配置。

3.2 核心差异点

  1. 沙箱密钥获取
    微信沙箱需先调用沙箱签名接口获取沙箱专用密钥(sandbox_signkey),后续接口用该密钥签名。

    // 示例:获取沙箱密钥
    WXPayConfig config = new MyWXPayConfig(); // 自定义配置类(含商户号、API密钥等)
    WXPay wxPay = new WXPay(config);
    Map<String, String> params = new HashMap<>();
    params.put("mch_id", config.getMchID());
    params.put("nonce_str", WXPayUtil.generateNonceStr());
    params.put("sign", WXPayUtil.generateSignature(params, config.getKey()));
    Map<String, String> result = wxPay.sandboxnewKey(params);
    String sandboxSignKey = result.get("sandbox_signkey"); // 沙箱密钥
    
  2. 接口调用
    微信支付接口(如 JSAPI 支付)需使用沙箱网关(https://api.mch.weixin.qq.com/sandboxnew/),并通过WXPay类(微信 SDK)调用。

    // 示例:创建JSAPI支付订单
    Map<String, String> data = new HashMap<>();
    data.put("body", "测试订单");
    data.put("out_trade_no", outTradeNo);
    data.put("total_fee", "1"); // 金额(单位:分)
    data.put("spbill_create_ip", "127.0.0.1");
    data.put("notify_url", "http://你的公网地址/pay/wx/notify");
    data.put("trade_type", "JSAPI");
    data.put("openid", "用户的openid"); // 微信用户唯一标识
    
    WXPay wxPay = new WXPay(new SandboxWXPayConfig(sandboxSignKey)); // 使用沙箱配置
    Map<String, String> result = wxPay.unifiedOrder(data);
    
  3. 异步通知处理
    与支付宝类似,需验证签名(用沙箱密钥),并返回"<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>"确认接收。

四、注意事项

  1. 签名验证:必须严格验证支付平台的通知签名,防止恶意请求伪造支付结果。
  2. 回调地址:异步通知地址(notifyUrl)需公网可访问(本地测试可使用 ngrok 等工具映射端口)。
  3. 幂等性处理:同一订单可能收到多次异步通知,需确保订单状态更新逻辑幂等(如通过订单号判断是否已处理)。
  4. 沙箱与正式环境隔离:通过配置文件区分沙箱和正式环境的参数(网关、密钥等),避免混淆。
  5. 异常处理:接口调用超时、网络错误等情况需重试,避免订单状态不一致。

通过以上步骤,可在 Java 项目中完整集成沙箱支付,验证支付全流程的正确性后,再切换至正式环境即可上线。

到此这篇关于java配置沙箱支付的实现示例的文章就介绍到这了,更多相关java 沙箱支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文