Java接入支付宝支付功能(APP支付)

客户就是上帝这句话一点都没错, 之前开发的一个专门接支付的项目,起初项目原型是接入支付宝支付,用的是客户公司的商户号. 后来因为种种原因导致客户商户号出现问题,联系我们看能不能把支付宝支付换成微信支付. 客户要求嘛, 改吧. 就把项目的支付从支付宝换到了微信.

本以为到此就结束了,没想到前几天 客户又要求换回支付宝,只不过用我们公司的商户.具体原因也说不清,反正就是不用微信支付了,又要换回支付宝, 没办法 改呗.

但是这次换支付宝我整整写了一个下午, 开发期间有几个需要注意的坑, 下面会说. 

首先准备工作:

一、appid, 登录支付宝开放平台点击右上角控制台

选择你要用的商户,点击进去

进去之后在页面左上角有商户的信息, 我圈出来的位置就是你的appid

二、public_key和private_key 接口加签方式(密钥/证书)

开发设置 > 开发信息 > 接口加签方式(密钥/证书)> 设置。

配置流程:

  1. 设置加签方式,选择密钥->下一步

  2. 生成密钥文件

    a.需要下载一个支付宝开放平台密钥工具.

         b.打开密钥工具,进入生成密钥功能

    c.加签方式选择密钥,加密算法选择RSA2

    d.点击生成密钥,工具会自动生成应用公钥(public_key)和应用私钥(private_key)点击打开文件位置就能找到生成的公私钥文件, 私钥一定妥善保管,代码里需要用也就是这个private_key

    e.复制应用公钥字符串

    f.返回开放平台控制台中,点击下一步

3.上传应用公钥,粘贴复制的应用公钥字符串,点击确认上传.此时需要输入短信验证码或支付密码完成安全验证.

4.密钥配置完成.

public_key就是你复制的应用公钥字符串,或者复制这个

private_key就是上面让你保存好的应用私钥

三、支付宝API文档

https://open.alipay.com/api

进去之后会有一些示例什么的,大家看看就行,要根据自己项目实际情况来看 需要对接哪些API, 我们只是对接了一个支付功能,目前退款和其他的API还没有接入 所以这期只写一下关于支付的功能.

点击查看文档进去之后会看到一些公共参数和请求参数

要想完整的接入支付宝,请求参数是必须规范的,一定要仔细看文档,懂了之后 再去写代码.

四、完善代码

ps:我们是SSM项目,没有用到pom,大家用boot项目的话,下面是sdk依赖,我们只是下的jar包

依赖:

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

jar包:

https://pan.baidu.com/s/1bnmfYNRaDmLe27BFlN0gTA

提取码:1234 /没有

创建AlipayConfig类

public class AlipayConfig {
  
  
  //2023/12/11 观塘新标准测试 商户appid
  public static String APPID = "你的appid";
  //支付宝私钥
  public static String RSA_PRIVATE_KEY = "你的支付宝私钥";
  //支付宝公钥
  public static String ALIPAY_PUBLIC_KEY = "你的支付宝公钥";

  // 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
  public static String off_line_notify_url ="支付宝异步通知请求地址,必须是外网可以访问的接口";
  // 商户可以自定义同步跳转地址
  public static String return_url = "无用";
  // 线上请求网关地址
  public static String URL = "https://openapi.alipay.com/gateway.do";
  // 沙箱环境网关地址
  // public static String URL = "https://openapi.alipaydev.com/gateway.do";
  // 编码
  public static String CHARSET = "UTF-8";
  //  public static String CHARSET = "GBK";
  // 返回格式
  public static String FORMAT = "json";
  // 日志记录目录
  public static String log_path = "/log";
  // RSA2
  public static String SIGNTYPE = "RSA2";
}

下单接口

String doctor_spwz_money = UserService.AdvisoryCodePrice();
String tradeno = getTradeNo();
if (doctor_spwz_money.equals("0") || null == doctor_spwz_money) {
      map.put("msg", "金额不能为0");
      map.put("code", "-5");
      return map;
    }
map.put("code_pay_userid", code_pay_userid);
    map.put("code_money", doctor_spwz_money);
    map.put("username", username);
    map.put("tradeno", tradeno);
    //创建订单
    int i = UserService.insertOrderinfo(map);
    //构建支付宝返回类
    AlipayTradeAppPayResponse response = null;
    // 实例化客户端
    AlipayClient alipayClient = new DefaultAlipayClient(
       "https://openapi.alipay.com/gateway.do", AlipayConfig.APPID,
        AlipayConfig.RSA_PRIVATE_KEY, "json", AlipayConfig.CHARSET,
        AlipayConfig.ALIPAY_PUBLIC_KEY, "RSA2");
    // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
    AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
    //构建请求参数
    JSONObject bizContent = new JSONObject();
    bizContent.put("out_trade_no", tradeno);
    bizContent.put("total_amount", doctor_spwz_money);
    bizContent.put("subject", subject);
    bizContent.put("product_code", "QUICK_MSECURITY_PAY");
    request.setNotifyUrl(AlipayConfig.off_line_notify_url);
    request.setBizContent(bizContent.toString());
    try {
      //这里和普通的接口调用不同,使用的是sdkExecute
      response = alipayClient.sdkExecute(request);
      System.out.println("---------------------" + response.getBody() + "---------------------");// 就是orderString
      // 可以直接给客户端请求,无需再做处理。
    } catch (AlipayApiException e) {
      e.printStackTrace();
    }
    if (response.isSuccess()) {
      map.put("code_money",doctor_spwz_money);
      map.put("data", response.getBody());
      map.put("code", "200");
      map.put("msg", "下单成功");
      return map;
    }else{
      map.put("code", "5000");
      map.put("msg", "下单失败");
      return map;
    }

public static String getTradeNo() {
    // 自增8位数 00000001
    return "TNO" + formatDate(new Date(), "yyyyMMddHHmmssSSS") + "00000001";
  }
  //自己定义随机字符串就行

异步通知回调接口

// 异步回调
  @SuppressWarnings({ "unused", "rawtypes" })
  @RequestMapping(value = "NotifyUrl", method = RequestMethod.POST)
  @ResponseBody
  public String NotifyUrl(HttpServletRequest request) throws IOException {

    System.out.println("******************************************************NotifyUrl***************************************************");
    
    final Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        final String paramsJson = JSON.toJSONString(params);
        logger.info("支付宝回调,{}" , paramsJson);
        try {
            AlipayConfig alipayConfig = new AlipayConfig();// 支付宝配置
            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY,
                AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
            if (signVerified) {
                logger.info("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success,校验失败返回failure
                this.check(params);
                // 另起线程处理业务
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        AlipayNotifyParam param = buildAlipayNotifyParam(params);
                        String trade_status = param.getTrade_status();
                        // 支付成功
                        if (trade_status.equals("TRADE_SUCCESS")
                                || trade_status.equals("TRADE_FINISHED")) {
                            // 处理支付成功逻辑
                            try {
                              String out_trade_no = param.getOut_trade_no();
                              String trade_no = param.getTrade_no();
                              int i = UserService.udcode_paystatus(out_trade_no, trade_no);
                          if (i != 1) {
                            logger.info("订单支付成功,但信息保存失败");
                          } else {
                            logger.info("success");
                          }
                            } catch (Exception e) {
                                logger.error("支付宝回调业务处理报错,params:" + paramsJson, e);
                            }
                        } else {
                            logger.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}",trade_status,paramsJson);
                        }
                    }
                });
                //验签成功之后 直接return success
                logger.info("验签成功 return success,业务正在处理中");
                return "success";
            } else {
                logger.info("支付宝回调签名认证失败,signVerified=false, paramsJson:{}", paramsJson);
                return "failure";
            }
        } catch (AlipayApiException e) {
            logger.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage());
            return "failure";
        }
    }

    // 将request中的参数转换成Map
    private static 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;
    }

    private AlipayNotifyParam buildAlipayNotifyParam(Map<String, String> params) {
        String json = JSON.toJSONString(params);
        return JSON.parseObject(json, AlipayNotifyParam.class);
    }

    /**
     * 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时,支付宝才会认定为买家付款成功。
     * 
     * @param params
     * @throws AlipayApiException
     */
    private void check(Map<String, String> params) throws AlipayApiException {      
        String outTradeNo = params.get("out_trade_no");

        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
        Order order = getOrderByOutTradeNo(outTradeNo);//这个自己写查询
        if (order == null) {
            throw new AlipayApiException("out_trade_no错误");
        }       

        // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额)
        if (!params.get("total_amount").toString().equals(order.getPayPrice().toString())) {
          throw new AlipayApiException("error total_amount");
    }

        // 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
        // 第三步省略

        // 4、验证app_id是否为该商户本身。
        if (!params.get("app_id").equals(AlipayConfig.APPID)) {
            throw new AlipayApiException("app_id不一致");
        }
    }

private Order getOrderByOutTradeNo(String outTradeNo){
      return UserService.getOrderByOutTradeNo(outTradeNo);
    }

到这代码就完成了.

代码中的import:


import java.io.IOException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.inject.Qualifier;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import net.sf.json.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.ecg.pay.AlipayNotifyParam;
import com.ecg.service.WeChatPayService;
import com.ecg.util.AlipayConfig;
import com.ecg.util.ApplicationContextHelperUtil;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.yc.pay.core.PayApiProvider;
import com.yc.pay.core.wechat.v3.WeChatApiProvider;
import com.yc.pay.core.wechat.v3.WeChatDirectPayApi;
import com.yc.pay.core.wechat.v3.entity.ResponseSignVerifyParams;
import com.yc.pay.vo.Amount;
import com.yc.pay.vo.PayParams;

总结: 其实接支付宝支付并不难,只是比较繁琐. 我遇到的问题就是在用户支付成功之后的异步通知回调接口时,处理请求体中的request.getParameterMap(); 

循环处理待验签字符串的时候,处理支付宝回调请求里的数据时,没有处理好,下面把出现问题的代码贴过来, 大家可以看一下具体问题出现在哪我就不做解释了!可以在评论区讨论一下


Map requestParams = request.getParameterMap();
    for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
      String name = (String) iter.next();
      String[] values = (String[]) requestParams.get(name);
      String valueStr = "";
      for (int i = 0; i < values.length; i++) {
        valueStr = (i == values.length - 1) ? valueStr + values[i]
            : valueStr + values[i] + ",";
      }
      // 订单编号
      out_trade_no = requestParams.get("out_trade_no");
      // 订单交易号
      trade_no = requestParams.get("trade_no");

      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//
      // 设置日期格式
      String format = df.format(new Date());// new Date()为获取当前系统时间,支付时间

      // 乱码解决,这段代码在出现乱码时使用。
      // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
      param.put(name, valueStr);
    } 
    // 获取支付宝POST过来反馈信息
    Map<String, String> params = new HashMap<String, String>();

    boolean flag = true;
    try {
      flag = AlipaySignature.rsaCheckV1(param,
          AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET,
          "RSA2");

    } catch (AlipayApiException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

AlipaySignature.rsaCheckV1 调用支付宝验签方法时报的错

好了感谢大家收看

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值