介绍

使用蚂蚁金服的开放平台中的沙箱环境我们可以十分便捷的使用支付宝的支付进行测试,这里我只记录一下我自己再Spring Boot里面的开发流程。记得开发前先登陆支付宝开启沙箱。

支付宝的开发平台:https://openhome.alipay.com

开发

1.项目导入Maven包

在底下的链接可以找到对应语言的SDK包下载使用:https://docs.open.alipay.com/54/103419

我这里Java导入Maven包。

pom.xml

<!– 支付宝SDK –>
<dependency>
  <groupId>com.alipay.sdk</groupId>
  <artifactId>alipay-sdk-java</artifactId>
  <version>3.7.73.ALL</version>
</dependency>

2.配置密钥

官网教程:https://docs.open.alipay.com/291/105971/

主要就是先下载支付宝的密匙加密软件出一对密钥。

然后把商户应用公匙放在支付宝的沙箱环境中。

3.基础配置类

创建对应的基础配置类,填上属于自己的信息。

AlipayConfig.java

package com.liuliu.com.util;
/* *
 *类名:AlipayConfig
 *功能:基础配置类
 *详细:设置帐户有关信息及返回路径
 *修改日期:2017-04-05
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */

public class AlipayConfig {

//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    //商户UID
    public static String seller_id = "";

    // 应用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://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    public static String 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 log_path = "https://openapi.alipaydev.com/gateway.do";

//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

}

4.发送支付请求

我这里是service层处理生成支付的表单然后通过controller给前台,然后前台自动转跳到支付宝的支付链接。

service类

public String aliPayPayment(String oCode, BigDecimal totalAmount) {
// TODO 请求支付宝支付
    AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
    AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
    alipayRequest.setReturnUrl(AlipayConfig.return_url);
    alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
//商户订单号,商户网站订单系统中唯一订单号,必填
    String out_trade_no = oCode;
//付款金额,必填
    String total_amount = totalAmount.setScale(2,BigDecimal.ROUND_HALF_UP).toString();
//订单名称,必填
    String subject = "购物商城";
//商品描述,可空
    List<CartPropertyProductVO> cartPropertyProductVOList = this.shoppingCartInfoMapper.selectShoppingCartSettlement(propertyList);
    String body = "";
    alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
            + "\"total_amount\":\""+ total_amount +"\","
            + "\"subject\":\""+ subject +"\","
            + "\"body\":\""+ body +"\","
            + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求
    try {
        return alipayClient.pageExecute(alipayRequest).getBody(); //注意此处生成的是表单
    } catch (AlipayApiException e) {
// TODO Auto-generated catch block
        e.printStackTrace();
        return null;
    }
}
// 将request中的参数转换成Map
public Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
    Map<String, String> retMap = new HashMap<String, String>();

    Set<Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();

    for (Entry<String, String[]> entry : entrySet) {
        String name = entry.getKey();
        String[] values = entry.getValue();
        int valLen = values.length;
        if (valLen == 1) {
            retMap.put(name, values[0]);
        } else if (valLen > 1) {
            StringBuilder sb = new StringBuilder();
            for (String val : values) {
                sb.append(",").append(val);
            }
            retMap.put(name, sb.toString().substring(1));
        } else {
            retMap.put(name, "");
        }
    }
    return retMap;
}

controller类

@RequestMapping("user_settlement_payment")
public String user_settlement_payment(int usadId,int integralNumber, String oiMessage, ModelMap map, HttpSession session) {
    String paymentForm = this.service.aliPayPayment(String oCode, BigDecimal totalAmount);
    if(paymentForm!=null){
        map.put("paymentForm",paymentForm);
        return "user/payment/payment"; //表单成功给到的页面,注意表单会自动提交到支付宝服务器,所有此页面把paymentForm给到页面的body里就行
    }
    return "user/payment/error"; 生成表单失败给到的页面
}

5.支付成功处理请求

因为根据支付宝的官网文档,我们是用异步链接来判断此订单是否支付成功,而不是通过同步链接来确定,所有同步链接单纯做个页面给用户看就好。

controller类

@RequestMapping("notify_url")
@ResponseBody
public void notify_url(HttpServletRequest request,BigDecimal totalAmountBig) {
//可以判断是否付款成功然后插入日志。
    this.alipayNotify(request); //异步的链接
}

@RequestMapping("return_url")
public String return_url() {
    return "user/payment/success"; //同步的页面
}

service类

/**
 需要严格按照如下描述校验通知数据的正确性:
 1.商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号;
 2.判断 total_amount 是否确实为该订单的实际金额(即商户订单创建时的金额);
 3.校验通知中的 seller_id(或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方(有的时候,一个商户可能有多个 seller_id/seller_email);
 4.验证 app_id 是否为该商户本身。
 上述 1、2、3、4 有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。
 **/
@Override
public String alipayNotify(HttpServletRequest request) {
// TODO 处理支付宝异步返回
    Map<String, String> params = this.convertRequestParamsToMap(request,BigDecimal totalAmountBig); // 将异步通知中收到的待验证所有参数都存放到map中
// 调用SDK验证签名
    try {
        boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key,
                AlipayConfig.charset, AlipayConfig.sign_type);
        if (signVerified) {
            if(order!=null) {
                BigDecimal aliTotalAmountBig=new BigDecimal(params.get("total_amount")).setScale(2,BigDecimal.ROUND_HALF_UP);
                if(totalAmountBig.compareTo(aliTotalAmountBig)==0) {
                    if(params.get("seller_id").equals(AlipayConfig.seller_id)) {
                        if(params.get("app_id").equals(AlipayConfig.app_id)) {
                            return "success";
//注意此处,接收到异步通知并验签通过后,一定要检查通知内容,包括通知中的 app_id、out_trade_no、total_amount 是否与请求中的一致,并根据 trade_status 进行后续业务处理。
                        }
                    }
                }
            }
        }
    } catch (AlipayApiException e) {
// TODO 抛出异常
        e.printStackTrace();
    }
    return "error";
}

后面就是处理自己要的页面,需要拿到回传的参数的话通过params.get(“回来的参数名称”)即可拿到。