环境介绍
开发环境
实际生产环境,每次测试需要0.01元的巨资;使用的为前端为uiapp 后端为spring boot框架
前期准备工作:
通过支付宝平台注册获取商户appid,使用密钥工具(Windows,ios)生成商家的公私密钥,通过用户的公钥在支付宝端进行注册,形成支付宝公钥
支付宝密钥支付流程
###在pom中导入maven依赖
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.10.97.ALL</version>
</dependency>
以下为配置类
import org.springframework.context.annotation.Configuration;
@Configuration
public class AliPayConfig {
// 商户appid
public static String APPID = "保密";
// 私钥 pkcs8格式的
public static String RSA_PRIVATE_KEY = "保密";
// 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://保密:8085/Alipay/notify";
// 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址
public static String return_url = "http://商户网关地址/alipay.trade.wap.pay-JAVA-UTF-8/return_url.jsp";
// 请求网关地址
public static String gatewayUrl = "https://openapi.alipay.com/gateway.do";
// 编码
public static String CHARSET = "UTF-8";
// 返回格式
public static String FORMAT = "json";
// 支付宝公钥
public static String ALIPAY_PUBLIC_KEY = "保密";
// 日志记录目录
public static String log_path = "/log";
// RSA2
public static String SIGNTYPE = "RSA2";
}
还是配置类,主要是通过Bean来管理了创建DefaultAlipayClient,避免之后每次调用都要写一堆参数去new
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Slf4j
@Component
@Configuration
public class AliPayClientConfig {
@Resource
private AliPayConfig alipayConfig;
/**
* 支付宝gatewayUrl
*/
private String gatewayUrl = alipayConfig.gatewayUrl;
/**
* 商户应用id
*/
private String appid = alipayConfig.APPID;
/**
* RSA私钥,用于对商户请求报文加签
*/
private String appPrivateKey = alipayConfig.RSA_PRIVATE_KEY;
/**
* 支付宝RSA公钥,用于验签支付宝应答
*/
private String alipayPublicKey = alipayConfig.ALIPAY_PUBLIC_KEY;
/**
* 签名类型
*/
private String signType = alipayConfig.SIGNTYPE;
/**
* 格式
*/
private String formate = alipayConfig.FORMAT;
/**
* 编码
*/
private String charset = alipayConfig.CHARSET;
/**
* 同步地址
*/
private String returnUrl = alipayConfig.return_url;
/**
* 异步地址
*/
private String notifyUrl = alipayConfig.notify_url;
/**
* 最大查询次数
*/
private static int maxQueryRetry = 5;
/**
* 查询间隔(毫秒)
*/
private static long queryDuration = 5000;
/**
* 最大撤销次数
*/
private static int maxCancelRetry = 3;
/**
* 撤销间隔(毫秒)
*/
private static long cancelDuration = 3000;
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getAppPrivateKey() {
return appPrivateKey;
}
public void setAppPrivateKey(String appPrivateKey) {
this.appPrivateKey = appPrivateKey;
}
public String getAlipayPublicKey() {
return alipayPublicKey;
}
public void setAlipayPublicKey(String alipayPublicKey) {
this.alipayPublicKey = alipayPublicKey;
}
public String getSignType() {
return signType;
}
public void setSignType(String signType) {
this.signType = signType;
}
public String getFormate() {
return formate;
}
public void setFormate(String formate) {
this.formate = formate;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public String getGatewayUrl() {
return gatewayUrl;
}
public void setGatewayUrl(String gatewayUrl) {
this.gatewayUrl = gatewayUrl;
}
public String getReturnUrl() {
return returnUrl;
}
public void setReturnUrl(String returnUrl) {
this.returnUrl = returnUrl;
}
public String getNotifyUrl() {
return notifyUrl;
}
public void setNotifyUrl(String notifyUrl) {
this.notifyUrl = notifyUrl;
}
@Bean
public AlipayClient alipayClient(){
return new DefaultAlipayClient(this.getGatewayUrl(),
this.getAppid(),
this.getAppPrivateKey(),
this.getFormate(),
this.getCharset(),
this.getAlipayPublicKey(),
this.getSignType());
}
}
支付宝支付Controller
C层本来不该写业务逻辑的,那时为了测试方便就没改回来,见谅,我还是个小菜。
请求参数之后我会封装一下,大家勉强看看吧,支付部分都通了
import com.commerce.mall.common.api.CommonResult;
import com.commerce.mall.mapper.OmsOrderMapper;
import com.commerce.mall.model.OmsOrder;
import com.commerce.mall.model.OmsOrderExample;
import com.commerce.mall.portal.service.AlipayOrderService;
import io.micrometer.core.instrument.util.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
/**
* 支付宝支付Controller
*/
@Api(tags = "AlipayOrderController", description = "支付宝支付相关接口 测试中")
@RestController
@SpringBootApplication
@RequestMapping("/Alipay")
public class AliPayOrderController {
@Autowired
private AlipayOrderService alipayOrderService;
@Autowired
private OmsOrderMapper omsOrderMapper;
/**
* 创建订单
*/
@ApiOperation(value = "创建订单", notes = "支付宝支付创建订单")
@PostMapping("/createOrder")
public CommonResult createOrder(@ApiParam(value = "说明:商户网站唯一订单号") @RequestParam String orderSn,
@ApiParam(value = "说明:订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]") @RequestParam Double total_amount,
@ApiParam(value = "说明:商品的标题/交易标题/订单标题/订单关键字等") @RequestParam String subject,
@ApiParam(value = "商品描述") @RequestParam String body) {
try {
// 1、验证订单是否存在
OmsOrderExample omsOrder = new OmsOrderExample();
omsOrder.createCriteria().andOrderSnEqualTo(orderSn);
long count = omsOrderMapper.countByExample(omsOrder);
int newcount = (int)count;
if(newcount < 1){return CommonResult.failed("订单不存在,订单生成失败");}
// 2、创建支付宝订单
String orderStr = alipayOrderService.createOrder(orderSn, total_amount, subject,body);
return CommonResult.success("data", orderStr);
} catch (Exception e) {
return CommonResult.failed("订单生成失败"+e);
}
}
/**
* 支付异步通知
* 接收到异步通知并验签通过后,一定要检查通知内容,
* 包括通知中的app_id、out_trade_no、total_amount是否与请求中的一致,并根据trade_status进行后续业务处理。
* https://docs.open.alipay.com/194/103296
*/
@ApiOperation(value = "支付异步通知", notes = "接收到异步通知并验签通过后,一定要检查通知内容")
@PostMapping("/notify")
public String notify(HttpServletRequest request) {
// 验证签名,确保是支付宝返回的
boolean flag = alipayOrderService.rsaCheckV1(request);
if (flag) {
String tradeStatus = request.getParameter("trade_status"); // 交易状态
String outTradeNo = request.getParameter("out_trade_no"); // 商户订单号
String tradeNo = request.getParameter("trade_no"); // 支付宝订单号
String totalAmount = request.getParameter("total_amount"); // 本次交易支付的订单金额
String sellerId = request.getParameter("seller_id"); // 收款支付宝账号对应的支付宝唯一用户号
String notify_time = request.getParameter("notify_time"); // 通知的发送时间。格式为yyyy-MM-dd HH:mm:ss
String trade_status = request.getParameter("trade_status"); // 交易目前所处的状态
/**
* 还可以从request中获取更多有用的参数,自己尝试
*/
boolean notify = alipayOrderService.notify(tradeStatus, outTradeNo, tradeNo,totalAmount,sellerId,notify_time,trade_status);
if(notify){
return "success";
}
}
return "fail";
}
@ApiOperation(value = "查询订单", notes = "查询订单,半成品,禁止调用")
@PostMapping("/cheak")
public CommonResult checkAlipay(@ApiParam(value = "订单号") @RequestParam String orderNo) {
if(StringUtils.isBlank(orderNo)){
return CommonResult.failed("订单编号不能为空");
}
String aByte = alipayOrderService.checkAlipay(orderNo);
return CommonResult.success("data",aByte);
}
@ApiOperation(value = "退款", notes = "退款,半成品")
@PostMapping("/refund")
public CommonResult refund(@ApiParam(value = "订单号") @RequestParam String orderNo,
@ApiParam(value = "退款金额") @RequestParam double amount,
@ApiParam(value = "退款原因") @RequestParam(required = false) String refundReason) {
if(StringUtils.isBlank(orderNo)){
return CommonResult.failed("订单编号不能为空");
}
if(amount <= 0){
return CommonResult.failed("退款金额必须大于0");
}
String refund = alipayOrderService.refund(orderNo, amount, refundReason);
if (StringUtils.isBlank(refund)){return CommonResult.failed("失败");}
return CommonResult.success("退款成功");
}
}
支付宝管理Service实现类(Service没啥看的,就不贴了)
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.commerce.mall.mapper.OmsAlipayFlowMapper;
import com.commerce.mall.mapper.OmsOrderMapper;
import com.commerce.mall.model.*;
import com.commerce.mall.portal.config.AliPayClientConfig;
import com.commerce.mall.portal.service.AlipayOrderService;
import io.micrometer.core.instrument.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 支付宝管理Service实现类
*/
@Service
public class AlipayOrderServiceImpl implements AlipayOrderService {
private final Logger logger = LoggerFactory.getLogger(AlipayOrderServiceImpl.class);
@Autowired
private AliPayClientConfig alipayClientConfig;
@Autowired
private OmsOrderMapper omsOrderMapper;
@Autowired
private OmsAlipayFlowMapper omsAlipayFlowMapper;
@Override
public String createOrder(String orderSn, Double total_amount, String subject, String body) throws AlipayApiException {
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(orderSn);
model.setSubject(subject);
model.setTotalAmount(String.valueOf(total_amount));
model.setBody(body);
//model.setPassbackParams("公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数");
model.setProductCode("QUICK_MSECURITY_PAY");
// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天 (屁股后面的字母一定要带,不然报错)
model.setTimeoutExpress("30m"); //
//实例化具体API对应的request类,类名称和接口名称对应,app支付接口2.0
//https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay
AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest();
ali_request.setBizModel(model);
ali_request.setNotifyUrl(alipayClientConfig.getNotifyUrl());// 回调地址
//就是orderString 可以直接给客户端请求,无需再做处理。
try {
// 注意这里是sdkExecute方法, 如果是网页支付的话调用pageExecute方法
return alipayClientConfig.alipayClient().sdkExecute(ali_request).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("支付异常");
}
}
@Override
public boolean notify(String tradeStatus, String orderNo, String tradeNo,String totalAmount,String sellerId,String notify_time,String trade_status) {
if ("TRADE_FINISHED".equals(tradeStatus)
|| "TRADE_SUCCESS".equals(tradeStatus)) {
// 支付成功,根据业务逻辑修改相应数据的状态
//修改订单表状态
OmsOrder omsOrder = new OmsOrder();
omsOrder.setId(Long.parseLong(orderNo));
//支付方式:0->未支付;1->支付宝;2->微信
omsOrder.setPayType(1);
//订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单;6->售后
omsOrder.setStatus(1);
int i = omsOrderMapper.updateByPrimaryKeySelective(omsOrder);
//创建支付宝流水记录
OmsAlipayFlow alipayFlow = new OmsAlipayFlow();
alipayFlow.setOrderSn(orderNo);
alipayFlow.setTradeNo(tradeNo);
alipayFlow.setTotalAmount(new BigDecimal(totalAmount));
alipayFlow.setSellerId(Long.parseLong(sellerId));
//把string转化为date
try {
alipayFlow.setNotifyTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(notify_time));
} catch (ParseException e) {
e.printStackTrace();
return false;
}
alipayFlow.setTradeStatus(trade_status);
int insert = omsAlipayFlowMapper.insert(alipayFlow);
if (i>0 && insert>0) {
return true;
}
}
return false;
}
//开放平台 SDK 提供了 AlipaySignature.rsaCertCheckV1 方法,可以使用该方法对通知报文验签。
//生活号异步通知需要使用 AlipaySignature.rsaCertCheckV2 方法,会保留 sign_type 参数参与验签。
@Override
public boolean rsaCheckV1(HttpServletRequest request){
try {
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) 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] + ",";
}
params.put(name, valueStr);
}
boolean verifyResult = AlipaySignature.rsaCheckV1(params, alipayClientConfig.getAlipayPublicKey(), alipayClientConfig.getCharset(), alipayClientConfig.getSignType());
return verifyResult;
} catch (AlipayApiException e) {
logger.debug("verify sigin error, exception is:{}", e);
return false;
}
}
@Override
public String checkAlipay(String outTradeNo) {
logger.info("==================向支付宝发起查询,查询商户订单号为:"+outTradeNo);
try {
AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest();
alipayTradeQueryRequest.setBizContent("{" +
"\"out_trade_no\":\""+outTradeNo+"\"" +
"}");
AlipayTradeQueryResponse alipayTradeQueryResponse = alipayClientConfig.alipayClient().execute(alipayTradeQueryRequest);
if(alipayTradeQueryResponse.isSuccess()){
//业务逻辑具体看个人 这不展示了
switch (alipayTradeQueryResponse.getTradeStatus()) // 判断交易结果
{
case "TRADE_FINISHED": // 交易结束并不可退款
// 修改订单状态的代码
return "TRADE_FINISHED 交易结束并不可退款";
// break;
case "TRADE_SUCCESS": // 交易支付成功
// 修改订单状态的代码
return "TRADE_SUCCESS 交易支付成功";
// break;
case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款
// 修改订单状态的代码
return "TRADE_CLOSED 未付款交易超时关闭或支付完成后全额退款";
// break;
case "WAIT_BUYER_PAY": // 交易创建并等待买家付款
// 修改订单状态的代码
return "WAIT_BUYER_PAY 交易创建并等待买家付款";
// break;
default:
break;
}
// 更新表记录
// return alipaymentOrder.getTradeStatus();
return (alipayTradeQueryResponse).toString();
} else {
logger.info("==================调用支付宝查询接口失败!");
return "调用支付宝查询接口失败";
}
} catch (AlipayApiException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public String refund(String orderNo, double amount, String refundReason) {
AlipayTradeRefundModel model=new AlipayTradeRefundModel();
// 商户订单号
model.setOutTradeNo(orderNo);
// 退款金额
model.setRefundAmount(String.valueOf(amount));
// 退款原因
model.setRefundReason(refundReason);
// 退款订单号(同一个订单可以分多次部分退款,当分多次时必传)
// model.setOutRequestNo(UUID.randomUUID().toString());
AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
alipayRequest.setBizModel(model);
AlipayTradeRefundResponse alipayResponse = null;
try {
alipayResponse = alipayClientConfig.alipayClient().execute(alipayRequest);
} catch (AlipayApiException e) {
logger.error("订单退款失败,异常原因:{}", e);
}
if(alipayResponse != null){
String code = alipayResponse.getCode();
String subCode = alipayResponse.getSubCode();
String subMsg = alipayResponse.getSubMsg();
if("10000".equals(code)
&& StringUtils.isBlank(subCode)
&& StringUtils.isBlank(subMsg)){
// 表示退款申请接受成功,结果通过退款查询接口查询
// 修改用户订单状态为退款
OmsOrder omsOrder = new OmsOrder();
omsOrder.setId(Long.parseLong(orderNo));
//订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单;6->售后
omsOrder.setStatus(4);
int i = omsOrderMapper.updateByPrimaryKeySelective(omsOrder);
//创建支付宝流水记录
OmsAlipayFlow alipayFlow = new OmsAlipayFlow();
//TRADE_CLOSED 未付款交易超时关闭,或支付完成后全额退款
alipayFlow.setTradeStatus("TRADE_CLOSED");
OmsAlipayFlowExample example = new OmsAlipayFlowExample();
example.createCriteria()
.andOrderSnNotEqualTo(orderNo);
int insert = omsAlipayFlowMapper.updateByExampleSelective(alipayFlow,example);
if (i>0 && insert>0) {
return "订单退款成功";
}
}
return null;
}
return null;
}
}