Java接入支付宝实现扫码支付
支付宝接入扫码支付还是比较简单的,为了方便开发还专门提供了沙箱环境,如果不想创建应用的可以直接使用沙箱环境的参数
提前准备
选择网页/移动应用,点击创建应用
填写完信息后点击立即创建,创建完成后点击开发设置,设置支付加签和密钥
加签密钥需要使用支付宝的密钥生成工具
密钥工具下载地址:小程序文档 - 支付宝文档中心
生成密钥
将生成的公钥填入支付宝开放平台,确认
然后点击提交审核
使用沙箱环境
如果自己没有申请应用的,可以使用沙箱环境进行开发
沙箱环境地址:
代码开发
引入pom
<!-- 支付宝支付包 --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.10.209.ALL</version> </dependency>yml配置文件
yml文件添加支付配置
alipay: # 支付宝网关名、partnerId和appId openApiDomain: https://openapi.alipay.com/gateway.do mcloudApiDomain: http://mcloudmonitor.com/gateway.do appId: 1235656 pid: 1556 # RSA私钥、公钥和支付宝公钥 #此处请填写你的应用私钥且转PKCS8格式 privateKey: #此处请填写你的应用公钥 publicKey: #SHA256withRsa对应支付宝公钥 alipayPublicKey: # 签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa signType: RSA2 #异步通知url(注意拦截器是否拦截) notifyUrl: http://8u862c.natappfree.cc/NBOSTA_WSBM/Alipay/ZFBcallbackAction.do
这些参数沙箱环境都已经配置好了,可以拿过来使用
读取配置文件参数:
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
//网关名称
private String openApiDomain;
private String mcloudApiDomain;
//app id
private String appId;
// pid
private String pid;
//私钥
private String privateKey;
//公钥
private String publicKey;
//SHA256withRsa对应支付宝公钥
private String alipayPublicKey;
//签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa
private String signType;
//回调地址
private String notifyUrl;
}
创建AlipayClient:
@Autowired
private final AlipayConfig aliPayConfig;
private AlipayClient getAlipayClient() {
return new DefaultAlipayClient(
aliPayConfig.getOpenApiDomain(),
aliPayConfig.getAppId(),
aliPayConfig.getPrivateKey(),
AlipayConstants.FORMAT_JSON,
AlipayConstants.CHARSET_UTF8,
aliPayConfig.getAlipayPublicKey(),
aliPayConfig.getSignType()
);
}
支付预下单
官方开发文档:小程序文档 - 支付宝文档中心
入参实体:
@Data
public class AliPayFaceToFaceModel {
private String outTradeNo;
private String subject;
private String totalAmount;
private String body;
}
出参实体:
@Data
public class AliPayResultModel {
//商户订单号
private String outTradeNo;
//支付 二维码地址
private String qrCode;
//支付宝交易号
private String tradeNo;
/**
*交易状态
* WAIT_BUYER_PAY(交易创建,等待买家付款)
* TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)
* TRADE_SUCCESS(交易支付成功)
* TRADE_FINISHED(交易结束,不可退款)
* 状态业务说明
* 1. 交易创建成功后,用户支付成功,交易状态转为 TRADE_SUCCESS(交易成功)。
* 2. 交易成功后,规定退款时间内没有退款,交易状态转为 TRADE_FINISHED(交易完成)。
* 3. 交易支付成功后,交易部分退款,交易状态为 TRADE_SUCCESS(交易成功)。
* 4. 交易成功后,交易全额退款,交易状态转为 TRADE_CLOSED(交易关闭)。
* 5. 交易创建成功后,用户未付款交易超时关闭,交易状态转为 TRADE_CLOSED(交易关闭)。
* 6. 交易创建成功后,用户支付成功后,若用户商品不支持退款,交易状态直接转为 TRADE_FINISHED(交易完成)。
* 注意:交易成功后部分退款,交易状态仍为 TRADE_SUCCESS(交易成功),如果一直部分退款退完所有交易金额则交易状态转为 TRADE_CLOSED(交易关闭),如果未退完所有交易金额,超过有效退款时间后交易状态转为 TRADE_FINISHED(交易完成)不可退款
*/
private String tradeStatus;
//交易金额
private String totalAmount;
//退款金额
private String refundFee;
// 本次退款是否发生了资金变化 Y: 资金发生变化,退款成功
private String fundChange;
//回调时返回请求传递时的body信息
private String body;
}
漏掉的方法
private AlipayTradePrecreateRequest getTradePreCreateRequest(AliPayFaceToFaceModel aliPayFaceToFaceModel) {
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.precreate(统一收单线下交易预创建(扫码支付)
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();//设置业务参数
model.setOutTradeNo(aliPayFaceToFaceModel.getOutTradeNo());//商户订单号,商户自定义,需保证在商户端不重复,如:20200612000001
model.setSubject(aliPayFaceToFaceModel.getSubject());//订单标题
model.setTotalAmount(aliPayFaceToFaceModel.getTotalAmount());//订单金额,精确到小数点后两位
model.setBody(aliPayFaceToFaceModel.getBody());//订单描述
request.setBizModel(model);
/*
异步通知地址,以http或者https开头的,商户外网可以post访问的异步地址,用于接收支付宝返回的支付结果,如果未收到该通知可参考该文档进行确认:https://opensupport.alipay.com/support/helpcenter/193/201602475759
*/
request.setNotifyUrl(aliPayConfig.getNotifyUrl());
return request;
}
开始扫码下单:
/**
* 支付宝预下单
*/
public AjaxResult aliPayPreorder(AliPayFaceToFaceModel aliPayFaceToFaceModel) {
log.info("支付宝预下单,商户订单号:{}",aliPayFaceToFaceModel.getOutTradeNo());
try {
AlipayClient alipayClient = getAlipayClient();
AlipayTradePrecreateRequest tradePreCreateRequest = getTradePreCreateRequest(aliPayFaceToFaceModel);
AlipayTradePrecreateResponse response = alipayClient.execute(tradePreCreateRequest);
log.info("支付宝预下单接口调用成功,返回参数:{}",response.getBody());
// System.out.println(response.getBody());
if(StringUtils.equalsAny(response.getCode(),"10000")){
AliPayResultModel resultModel=new AliPayResultModel();
resultModel.setOutTradeNo(response.getOutTradeNo());
resultModel.setQrCode(response.getQrCode());
return AjaxResult.success(resultModel);
}else{
return AjaxResult.error("获取支付二维码失败,错误信息:"+response.getSubMsg());
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("下单失败");
}
}
查询订单状态
/**
* 交易状态查询
* 可以查看以下帮助文档:
* 判断交易是否成功:https://opensupport.alipay.com/support/helpcenter/195/201602516393?ant_source=zsearch
* 状态ACQ.TRADE_NOT_EXIST(交易不存在)https://opensupport.alipay.com/support/helpcenter/89/201602475600?ant_source=zsearch
*/
public AjaxResult queryTrade(AliPayFaceToFaceModel aliPayFaceToFaceModel) {
try {
log.info("调用支付宝交易状态查询接口,单号:{}",aliPayFaceToFaceModel.getOutTradeNo());
AlipayClient alipayClient = getAlipayClient();
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.query(统一收单线下交易查询)
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
// 注:交易号(TradeNo)与订单号(OutTradeNo)二选一传入即可,如果2个同时传入,则以交易号为准
//支付接口传入的商户订单号。如:2020061601290011200000140004 **/
model.setOutTradeNo(aliPayFaceToFaceModel.getOutTradeNo());
// 异步通知/查询接口返回的支付宝交易号,如:2020061622001473951448314322 **/
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
log.info("调用支付宝交易状态查询接口成功,返回参数:{}",response.getBody());
if(response.isSuccess()){
AliPayResultModel resultModel=new AliPayResultModel();
resultModel.setOutTradeNo(response.getOutTradeNo());
resultModel.setTradeStatus(response.getTradeStatus());
resultModel.setTradeNo(response.getTradeNo());
return AjaxResult.success(resultModel);
}else{
log.info("调用支付宝交易状态查询接口失败,失败信息:{}",response.getSubMsg());
return AjaxResult.error("支付宝订单查询失败:"+response.getSubMsg());
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("订单查询异常!");
}
}
交易退款接口
退款可以部分退款,根据自己业务需求修改,我这里是全额退款。
/**
* 交易退款接口
*
* @param aliPayFaceToFaceModel 参数实体
* @author: zm
* @date: 2024/10/8 11:05
* @rerturn: java.lang.String
*/
public AjaxResult payTradeRefund(AliPayFaceToFaceModel aliPayFaceToFaceModel) {
if(aliPayFaceToFaceModel == null || StringUtils.isEmpty(aliPayFaceToFaceModel.getOutTradeNo())){
log.error("订单号为空,请检查参数");
return AjaxResult.error("订单号为空");
}
try {
log.info("调用支付宝退款接口,单号:{}",aliPayFaceToFaceModel.getOutTradeNo());
AlipayClient alipayClient = getAlipayClient();
AlipayTradeRefundRequest refundRequest=new AlipayTradeRefundRequest();
AlipayTradeRefundModel refundModel =new AlipayTradeRefundModel();
//商户订单号
refundModel.setOutTradeNo(aliPayFaceToFaceModel.getOutTradeNo());
//退款金额
refundModel.setRefundAmount(aliPayFaceToFaceModel.getTotalAmount());
//退款描述
refundModel.setRefundReason("正常退款");
refundRequest.setBizModel(refundModel);
AlipayTradeRefundResponse response=alipayClient.execute(refundRequest);
log.info("支付宝交易退款接口调用成功,返回参数:{}",response.getBody());
if (!response.isSuccess()) {
log.error("支付宝交易退款接口调用失败,单号:{}",aliPayFaceToFaceModel.getOutTradeNo());
return AjaxResult.error("退款失败,错误信息:"+response.getSubMsg());
}
AliPayResultModel resultModel=new AliPayResultModel();
resultModel.setOutTradeNo(response.getOutTradeNo());
resultModel.setRefundFee(response.getRefundFee());
resultModel.setTradeNo(response.getTradeNo());
resultModel.setFundChange(response.getFundChange());
return AjaxResult.success(resultModel);
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("退款异常!");
}
}
支付回调接口
/**
* 支付宝支付回调
* @param request 请求
* @param response 响应
* @author: zm
* @date: 2024/10/10
* @rerturn: void
*/
public void notifyUrl(HttpServletRequest request, HttpServletResponse response){
try {
log.info("接收到支付回调信息");
//获取支付宝公钥
String aliPayPublicKey = aliPayConfig.getAlipayPublicKey();
PrintWriter out;
out = response.getWriter();
//获取支付宝POST过来反馈信息
Map<String, String> params = new HashMap<String, String>();
Map requestParams = request.getParameterMap();
//循环遍历支付宝请求过来的参数存入到params中
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] + ",";
}
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//异步验签:切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
boolean flag = AlipaySignature.rsaCheckV1(params, aliPayPublicKey, "utf-8","RSA2");
if (flag){
log.info("验签成功,返回参数:{}",params.toString());
AliPayResultModel resultModel=new AliPayResultModel();
resultModel.setOutTradeNo(params.get("out_trade_no"));
resultModel.setTradeStatus(params.get("trade_status"));
resultModel.setTradeNo(params.get("trade_no"));
resultModel.setBody(params.get("body"));
//TODO 调用业务接口
out.write("success");
}else {
log.info("支付宝验签失败,请联系工作人员");
//验签失败该接口被别人调用
out.write("支付宝异步回调验签失败,请留意");
}
out.flush();
out.close();
}catch (Exception e) {
e.printStackTrace();
}
}
撤销订单接口
有业务需要的可以加上
/**
* 支付宝订单撤销(关闭订单)
* @param payModel 请求参数
* @author: zm
* @date: 2024/10/10
* @rerturn: void
*/
public AjaxResult payTradeCancel(AliPayFaceToFaceModel payModel){
log.info("调用支付宝订单撤销接口,单号:{}",payModel.getOutTradeNo());
AlipayClient alipayClient = getAlipayClient();
// 构造请求参数以调用接口
AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();
AlipayTradeCancelModel model = new AlipayTradeCancelModel();
// 设置原支付请求的商户订单号
model.setOutTradeNo(payModel.getOutTradeNo());
request.setBizModel(model);
try {
AlipayTradeCancelResponse response = alipayClient.execute(request);
log.info("调用支付宝订单撤销接口成功,返回参数:{}",response.getBody());
if (response.isSuccess()) {
return AjaxResult.success();
} else {
return AjaxResult.error("关闭订单失败,失败原因:"+response.getSubMsg());
}
} catch (AlipayApiException e) {
e.printStackTrace();
return AjaxResult.error("关闭订单异常!");
}
}
好了支付宝支付到此结束,有疑问的可以留言!
异步通知需要内网穿透切记、切记、切记
参考博文:Java接入支付宝支付超级详细教程——从入门到精通_java接入支付宝自动扣费-CSDN博客
原文地址:https://blog.csdn.net/z132411/article/details/143741147
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!