java实现支付宝app支付

上一篇介绍了java实现微信app支付,这篇介绍支付宝的app 支付,支付宝在支付方面比微信方便太多,如下:

引入maven依赖:

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

application.yml配置文件:

alipay:
  config:
    url: https://openapi.alipay.com/gateway.do
    appId: 2019123456474282
    privateKey: 商户自己生成的私钥
    pubKey: 支付宝公钥
    charset: UTF-8
    signType: RSA2
    notifyUrl: https://test.shuai.com/aliPayCallback
    format: json

支付宝配置文件:

package com.bcn.yeyks.apppay.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @Author fuchangshuai
 * @date 2019/3/6 10:02 AM
 */
@Data
@Component
@ConfigurationProperties(prefix = "alipay.config")
public class AlipayAppConfig {
    private String url;
    private String appId;
    private String privateKey;
    private String pubKey;
    private String charset;
    private  String signType;
    // 异步回调地址
    private String notifyUrl;
    private String vipNotifyUrl;
    // 返回格式
    private String format;
}

回调参数对象:

package com.bcn.yeyks.apppay.config;

import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

/**
 * @Author fuchangshuai
 * @date 2019/3/6 1:00 PM
 */
@Data
public class AlipayNotifyParam {
    private String appId;
    private String tradeNo; // 支付宝交易凭证号
    private String outTradeNo; // 原支付请求的商户订单号
    private String outBizNo; // 商户业务ID,主要是退款通知中返回退款申请的流水号
    private String buyerId; // 买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字
    private String buyerLogonId; // 买家支付宝账号
    private String sellerId; // 卖家支付宝用户号
    private String sellerEmail; // 卖家支付宝账号
    private String tradeStatus; // 交易目前所处的状态,见交易状态说明
    private BigDecimal totalAmount; // 本次交易支付的订单金额
    private BigDecimal receiptAmount; // 商家在交易中实际收到的款项
    private BigDecimal buyerPayAmount; // 用户在交易中支付的金额
    private BigDecimal refundFee; // 退款通知中,返回总退款金额,单位为元,支持两位小数
    private String subject; // 商品的标题/交易标题/订单标题/订单关键字等
    private String body; // 该订单的备注、描述、明细等。对应请求时的body参数,原样通知回来
    private Date gmtCreate; // 该笔交易创建的时间。格式为yyyy-MM-dd HH:mm:ss
    private Date gmtPayment; // 该笔交易的买家付款时间。格式为yyyy-MM-dd HH:mm:ss
    private Date gmtRefund; // 该笔交易的退款时间。格式为yyyy-MM-dd HH:mm:ss.S
    private Date gmtClose; // 该笔交易结束时间。格式为yyyy-MM-dd HH:mm:ss
    private String fundBillList; // 支付成功的各个渠道金额信息,array
    private String passbackParams; // 公共回传参数,如果请求时传递了该参数,则返回给商户时会在异步通知时将该参数原样返回。
}

支付宝app支付类:

package com.bcn.yeyks.apppay.service.impl;

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.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.bcn.yeyks.apppay.config.AlipayAppConfig;
import com.bcn.yeyks.apppay.service.AlipayService;
import com.bcn.yeyks.exception.ServiceException;
import com.bcn.yeyks.model.RefundRequestNo;
import com.bcn.yeyks.util.FormatUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 * @Author fuchangshuai
 * @date 2019/3/6 9:22 AM
 */
@Service
@Slf4j
public class AliPayServiceImpl implements AlipayService {

    @Autowired
    private AlipayAppConfig alipayAppConfig;
    private AlipayClient alipayClient;

    @PostConstruct
    public void init() {
        alipayClient = new DefaultAlipayClient(alipayAppConfig.getUrl(),
                alipayAppConfig.getAppId(),
                alipayAppConfig.getPrivateKey(),
                alipayAppConfig.getFormat(),
                alipayAppConfig.getCharset(),
                alipayAppConfig.getPubKey(),
                alipayAppConfig.getSignType());
    }

    @Override
    public String tradeAppPay(String orderNo, Integer totalFee) {
        log.info("支付宝app支付");
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("app支付");
        model.setSubject("App支付测试Java");
        model.setOutTradeNo(orderNo);
        model.setTotalAmount(FormatUtils.fenToYuan(totalFee));
        request.setBizModel(model);
        request.setNotifyUrl(alipayAppConfig.getNotifyUrl());
        AlipayTradeAppPayResponse response;
        try {
            response = alipayClient.sdkExecute(request);
        } catch (AlipayApiException e) {
            log.error("支付宝支付失败{}", e.getErrMsg());
            throw new ServiceException(e.getErrMsg());
        }
        if (response.isSuccess()) {
            log.info("调用成功" + response.getBody());
            return response.getBody();
        } else {
            log.error("调用失败" + response.getBody());
            return null;
        }
    }

    @Override
    public Boolean tradeRefund(String orderNo, Integer refundFee) {
        log.info("支付宝退款 {}");
        String outRequestNo = orderNo + RefundRequestNo.outNo;
        AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        model.setOutTradeNo(orderNo);
        model.setRefundAmount(FormatUtils.fenToYuan(refundFee));
        model.setRefundReason("正常退款");
        model.setOutRequestNo(outRequestNo);
        refundRequest.setBizModel(model);
        AlipayTradeRefundResponse response;
        try {
            response = alipayClient.execute(refundRequest);
        } catch (AlipayApiException e) {
            log.error("支付宝退款失败{}", e.getLocalizedMessage());
            return false;
        }
        if (response != null && response.getCode().equals("10000")) {
            log.info("调用成功" + response);
            return true;
        } else {
            log.error("调用失败" + response);
            return false;
        }
    }



}

支付宝回调:

package com.bcn.yeyks.apppay.controller;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.bcn.yeyks.apppay.config.AlipayAppConfig;
import com.bcn.yeyks.apppay.config.AlipayNotifyParam;
import com.bcn.yeyks.dal.domain.OrderInfo;
import com.bcn.yeyks.dal.domain.VipOrder;
import com.bcn.yeyks.service.OrderService;
import com.bcn.yeyks.service.VipOrderService;
import com.bcn.yeyks.util.FormatUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author fuchangshuai
 * @date 2019/3/6 11:36 AM
 */
@Slf4j
@RestController
public class AlipayCallbackController {

    private ExecutorService executorService = Executors.newFixedThreadPool(20);

    @Autowired
    private AlipayAppConfig alipayAppConfig;

    @Autowired
    private OrderService orderService;

    @Autowired
    private VipOrderService vipOrderService;

    @RequestMapping(value = "/aliPayCallback", method = RequestMethod.POST)
    @ResponseBody
    public String callback(HttpServletRequest request) {
        Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        String paramsJson = JSON.toJSONString(params);
        log.info("支付宝回调,{}", paramsJson);
        try {
            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayAppConfig.getPubKey(),
                    alipayAppConfig.getCharset(), alipayAppConfig.getSignType());
            log.info("signVerified={}", signVerified);
            if (signVerified) {
                log.info("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
                check(params);
                // 另起线程处理业务
                log.info("另起线程处理业务");
                executorService.execute(() -> {
                    AlipayNotifyParam param = buildAlipayNotifyParam(params);
                    String tradeStatus = param.getTradeStatus();
                    // 支付成功
                    if (tradeStatus.equals("TRADE_SUCCESS")) {
                        log.info("支付成功");
                        //TODO 用户处理自己逻辑
                    } else {
                        log.info("支付失败");
                        //TODO 用户处理自己逻辑
                    }
                });
                // 如果签名验证正确,立即返回success,后续业务另起线程单独处理
                return "success";
            } else {
                log.info("支付宝回调签名认证失败, paramsJson:{}", paramsJson);
                return "failure";
            }
        } catch (AlipayApiException e) {
            log.info("支付宝回调发生异常{}", e.getLocalizedMessage());
            return "failure";
        }
    }

    // 将request中的参数转换成Map
    private Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<>();
        //1.从支付宝回调的request域中取值
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = iter.next();
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            retMap.put(name, valueStr);
        }
        return retMap;
    }

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

    private void check(Map<String, String> params) throws AlipayApiException {
        String outTradeNo = params.get("out_trade_no");

        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
        OrderInfo order = orderService.selectOrderByOrderNo(outTradeNo);
        if (order == null) {
            throw new AlipayApiException("out_trade_no错误");
        }

        // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
        Integer totalAmount = FormatUtils.yuan2Fen(new BigDecimal(params.get("total_amount")));
        log.info("totalAmount{}", totalAmount);
        if (!totalAmount.equals(order.getSnapshotTotalFee())) {
            throw new AlipayApiException("error total_amount");
        }

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


}

FormatUtils工具类:

package com.yeyks.common.utils;

import java.math.BigDecimal;
import java.util.Map;

/**
 * @Author fuchangshuai
 * @date 2019/3/6 10:28 AM
 */
public class FormatUtils {

    /**
     * 1. 钱的元到分
     * 2. 佣金比例的百分之到万分之
     *
     * @param yuan
     * @return
     */
    public static int yuan2Fen(BigDecimal yuan) {
        if (yuan == null) return 0;
        return yuan.multiply(BigDecimal.valueOf(100)).intValue();
    }

    public static Integer yuanToFen(String yuan) {
        return (new BigDecimal(yuan)).setScale(2, 4).multiply(new BigDecimal(100)).intValue();
    }

    public static int yuan2Fen(Double yuan) {
        if (yuan == null) {
            return 0;
        }
        BigDecimal bigDecimal = new BigDecimal(String.valueOf(yuan));
        return yuan2Fen(bigDecimal);
    }

    public static int yuan2Fen(int yuan) {
        if (yuan <= 0) return 0;
        return yuan * 100;
    }

    /**
     * 1. 钱的元到分
     * 2. 佣金比例的万分之到百分之
     *
     * @param fen
     * @return
     */
    public static BigDecimal fen2Yuan(Integer fen) {
        if (fen == null) {
            return BigDecimal.valueOf(0);
        }
        return BigDecimal.valueOf(fen).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_FLOOR);
    }

    public static Integer fen2YuanInt(Integer fen) {
        if (fen == null) {
            return 0;
        }
        return fen / 100;
    }

    public static String fenToYuan(Integer fen) {
        BigDecimal bigDecimal = fen2Yuan(fen);
        bigDecimal = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal.toString();
    }

    /**
     * 返回 (金额 * 百分比)(分)
     *
     * @param amount 金额 分
     * @param rate   百分比
     * @return
     */
    public static Integer amountToRate(Integer amount, Integer rate) {
        if ((amount == null) || (rate == null)) {
            return null;
        }
        BigDecimal multiply = BigDecimal.valueOf(amount).multiply(BigDecimal.valueOf(rate).divide(BigDecimal.valueOf(100)));
        return multiply.intValue();
    }

    /**
     * 返回 (金额 * 万分比)(分)
     *
     * @param amount 金额 分
     * @param rate   万分比
     * @return
     */
    public static Integer amountToRateV2(Integer amount, Integer rate) {
        if ((amount == null) || (rate == null)) {
            return null;
        }
        BigDecimal multiply = BigDecimal.valueOf(amount).multiply(BigDecimal.valueOf(rate).divide(BigDecimal.valueOf(10000)));
        return multiply.intValue();
    }

}

到这里支付宝app支付实现成功,修改商户信息后直接可以引用实现,小伙伴们可以试试

 

 
  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值