一.准备
java接入支付宝需要引入Maven
代码语言:javascript复制<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>${alipay.sdk.version}</version>
</dependency>
二.创建配置类
我是直接把配置放在nacos配置中心上面
代码语言:javascript复制package com.jieyihua.pay.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* @program: JieYiHua-Cloud
* @description: 支付宝配置
* @author: LiYu
* @create: 2021-08-16 09:35
**/
@Component
@ConfigurationProperties(prefix = "alipay")
@Data
public class Alipay implements Serializable {
private static final long serialVersionUID = -582917908416008697L;
/**
* 服务地址
*/
private String serverUrl;
/**
* AppId
*/
private String appId;
/**
* 应用私钥
*/
private String privateKey;
/**
* 应用公钥
*/
private String publicKey;
/**
* 支付宝公钥
*/
private String alipayPublicKey;
/**
* 支付宝回调地址
*/
private String alipayNoticeUrl;
/**
* 公钥证书文件路径
*/
private String certPath;
/**
* 公钥证书文件路径
*/
private String alipayPublicCertPath;
/**
* 支付宝根证书文件路径
*/
private String rootCertPath;
/**
* 编码
*/
private String charset = "UTF-8";
/**
* 返回格式
*/
private String format = "json";
/**
* RSA2
*/
private String signType = "RSA2";
}
3.具体实现
代码语言:javascript复制package com.jieyihua.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* @program: JieYiHua-Cloud
* @description: 支付宝返回结果对应的参数类
* @author: LiYu
* @create: 2021-10-14 13:27
**/
@Data
@Accessors(chain = true)
public class AlipayNotifyParam implements Serializable {
private String appId;
/**
* 支付宝交易凭证号
*/
private String tradeNo;
/**
* 原支付请求的商户订单号
*/
private String outTradeNo;
/**
* 商户业务ID,主要是退款通知中返回退款申请的流水号
*/
private String outBizNo;
/**
* 买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字
*/
private String buyerId;
/**
* 买家支付宝账号
*/
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;
/**
* 该订单的备注、描述、明细等。对应请求时的body参数,原样通知回来
*/
private String body;
/**
* 该笔交易创建的时间。格式为yyyy-MM-dd HH:mm:ss
*/
private LocalDateTime gmtCreate;
/**
* 该笔交易的买家付款时间。格式为yyyy-MM-dd HH:mm:ss
*/
private LocalDateTime gmtPayment;
/**
* 该笔交易的退款时间。格式为yyyy-MM-dd HH:mm:ss.S
*/
private LocalDateTime gmtRefund;
/**
* 该笔交易结束时间。格式为yyyy-MM-dd HH:mm:ss
*/
private LocalDateTime gmtClose;
/**
* 支付成功的各个渠道金额信息,array
*/
private String fundBillList;
/**
* 公共回传参数,如果请求时传递了该参数,则返回给商户时会在异步通知时将该参数原样返回。
*/
private String passbackParams;
}
代码语言:javascript复制/**
* @program: JieYiHua-Cloud
* @description: 支付宝接口
* @author: LiYu
* @create: 2021-08-16 10:08
**/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/alipay")
public class AlipayController {
private final AliPayService aliPayService;
@ApiOperation("拉起支付")
@PostMapping("/pullUpPayment")
public AjaxResult pullUpPayment(@RequestBody PayMoneyReq payMoneyReq) throws AlipayApiException {
return aliPayService.pullUpPayment(payMoneyReq);
}
@ApiOperation("支付宝回调")
@PostMapping("/notifyParam")
public String notifyParam(HttpServletRequest request){
return aliPayService.notifyParam(request);
}
}
代码语言:javascript复制public interface AliPayService {
/**
* 拉起支付
* @param payMoneyReq 请求参数
* @return 签名
* @throws AlipayApiException 异常
*/
AjaxResult pullUpPayment(@RequestBody PayMoneyReq payMoneyReq) throws AlipayApiException;
/**
* 回调接口
* <pre>
* 第一步:验证签名,签名通过后进行第二步
* 第二步:按一下步骤进行验证
* 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时,支付宝才会认定为买家付款成功。
* </pre>
* @param request 请求头
* @return 结果
*/
String notifyParam(HttpServletRequest request);
}
代码语言:javascript复制/**
* @author liyu
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AliPayServiceImpl implements AliPayService {
private final Alipay alipay;
private static final Logger logger = LoggerFactory.getLogger(AlipayController.class);
private final ExecutorService executorService = Executors.newFixedThreadPool(20);
public AlipayClient getAlipayClient() {
return new DefaultAlipayClient(alipay.getServerUrl(),
alipay.getAppId(),
alipay.getPrivateKey(), JSON, CHARSET,
alipay.getPublicKey(), SIGN_TYPE);
}
@Override
public AjaxResult pullUpPayment(PayMoneyReq payMoneyReq) throws AlipayApiException {
//实例化客户端
AlipayClient alipayClient = getAlipayClient();
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
SnowflakeIdWorkerUtils snowflakeIdWorkerUtils = new SnowflakeIdWorkerUtils(0,0);
request.setNotifyUrl(alipay.getAlipayNoticeUrl());
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", snowflakeIdWorkerUtils.nextId());
bizContent.put("subject", "戒易花会员开通");
bizContent.put("product_code", ValueConstants.QUICK_MSECURITY_PAY);
bizContent.put("charset",alipay.getCharset());
bizContent.put("sign_type",alipay.getSignType());
bizContent.put("total_amount", "0.01");
//自定义参数携带 有需要携带的参数自己填入
JSONObject jsonObject = new JSONObject();
bizContent.put("passback_params",jsonObject);
request.setBizContent(bizContent.toString());
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
log.info("拉起支付宝调用返回路径{}",response.getBody());
String result = response.getBody();
if(response.isSuccess()){
//把result返回给app则可以调用支付宝
return AjaxResult.success("请求成功",result);
} else {
return AjaxResult.error("拉取支付宝失败!");
}
}
@Override
public String notifyParam(HttpServletRequest request){
// 将异步通知中收到的待验证所有参数都存放到map中
Map<String, String> params = convertRequestParamsToMap(request);
String paramsJson = JSONObject.toJSONString(params);
log.info("支付宝回调,{}", paramsJson);
try {
//验签
boolean signVerify = AlipaySignature.rsaCheckV1(params, alipay.getAlipayPublicKey(), alipay.getCharset(), alipay.getSignType());
if (signVerify) {
// 另起线程处理业务
executorService.execute(new Runnable() {
@Override
public void run() {
AlipayNotifyParam param = buildAlipayNotifyParam(params);
log.info("返回参数,{}",param);
String tradeStatus = param.getTradeStatus();
// 支付成功
if (ValueConstants.TRADE_SUCCESS.equals(tradeStatus)
|| ValueConstants.TRADE_FINISHED.equals(tradeStatus)) {
log.info("支付成功,处理业务")
} catch (Exception e) {
logger.error("支付宝回调业务处理报错,params:" paramsJson, e);
}
} else {
logger.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}",tradeStatus,paramsJson);
}
}
});
// 如果签名验证正确,立即返回success,后续业务另起线程单独处理
// 业务处理失败,可查看日志进行补偿,跟支付宝已经没多大关系。
return "success";
}
}catch (Exception ex) {
ex.printStackTrace();
return "fail";
}
return "fail";
}
public AlipayFundTransOrderQueryResponse transferQueryToResponse(AlipayFundTransOrderQueryModel model) throws AlipayApiException{
AlipayFundTransOrderQueryRequest request = new AlipayFundTransOrderQueryRequest();
request.setBizModel(model);
//实例化客户端
AlipayClient alipayClient = getAlipayClient();
return alipayClient.execute(request);
}
private static Map<String, String> getStringStringMap(HttpServletRequest request, Map<String, String> retMap) {
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.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 = JSONObject.toJSONString(params);
return JSONObject.parseObject(json, AlipayNotifyParam.class);
}
}