手机网站开源系统图片在线转外链
Uniapp Android SpringBoot3 对接支付宝支付(最新教程附源码)
- 1、效果展示
- 2、后端实现
- 2.1 引入支付宝SDK依赖 pom.xml
- 2.2 配置 application.yml
- 2.3 支付宝相关代码
- 2.3.1 AlipayConfig.java
- 2.3.2 ZfbPayConfig.java
- 2.3.3 支付接口
- 2.3.4 支付回调处理接口(用于支付成功后,支付宝通知我们支付信息)
- 2.3.4 退款接口
- 3 前端代码
- 3.1 支付接口调用
- 3.2 支付方法
- 3.2 退款接口调用
1、效果展示
效果展示
2、后端实现
2.1 引入支付宝SDK依赖 pom.xml
<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.40.0.ALL</version>
</dependency>
2.2 配置 application.yml
真实环境需要开通 APP 支付, 如果是沙箱环境的话不支持退款。
# 支付宝支付
alipay:server_url: https://openapi.alipay.com/gateway.doapp_id: # 你的 appIdprivate_key: # 你的私钥format: jsonsellerId: 2088722047235165charset: utf-8alipay_public_key: # 你的公钥sign_type: RSA2notifyUrl: http://wtw867.natappfree.cc/app/shop/order/zfb-pay/notify # 支付成功通知地址
2.3 支付宝相关代码
2.3.1 AlipayConfig.java
package com.zhong.config;import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Getter
@Setter
@ToString
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig extends com.alipay.api.AlipayConfig {private String serverUrl;private String appId;private String privateKey;private String format;private String charset;private String alipayPublicKey;private String signType;private String notifyUrl;
}
2.3.2 ZfbPayConfig.java
package com.zhong.config;import com.alipay.api.AlipayApiException;
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.zhong.model.entity.goods.GoodsOrderInfo;
import com.zhong.service.GoodsOrderInfoService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;@Configuration
@Component
@Data
@Slf4j
public class ZfbPayConfig {@AutowiredAlipayConfig alipayConfig;@Autowiredprivate GoodsOrderInfoService goodsOrderInfoService;private DefaultAlipayClient client() throws AlipayApiException {return new DefaultAlipayClient(alipayConfig);}public String pay(GoodsOrderInfo order) {String source = "";try {DefaultAlipayClient client = client();AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();model.setSubject("云尚社区");model.setOutTradeNo(order.getOrderNo());model.setTotalAmount(String.valueOf(0.01));// alipay 封装的接口调用AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();request.setBizModel(model);request.setNotifyUrl(alipayConfig.getNotifyUrl());AlipayTradeAppPayResponse response = client.sdkExecute(request);source = response.getBody();} catch (AlipayApiException e) {goodsOrderInfoService.noPay(order.getOrderNo()); // 支付失败更新订单状态,可以根据您的具体业务做出调整log.error("支付出现问题,详情:{}", e.getErrMsg());e.printStackTrace();}log.info(source);return source;}/*** 退款** @param tradeNo* @param totalAmount* @return*/public AlipayTradeRefundResponse refund(String tradeNo, String totalAmount) {try {DefaultAlipayClient client = client();AlipayTradeRefundModel alipayTradeRefundModel = new AlipayTradeRefundModel();alipayTradeRefundModel.setTradeNo(tradeNo);alipayTradeRefundModel.setRefundAmount(totalAmount);AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();request.setBizModel(alipayTradeRefundModel);AlipayTradeRefundResponse response = client.execute(request);return response;} catch (AlipayApiException e) {log.error("退款出现问题,详情:{}", e.getErrMsg());e.printStackTrace();}return null;}}
2.3.3 支付接口
全局统一返回结果类
package com.zhong.result;import lombok.Data;/*** 全局统一返回结果类*/
@Data
public class Result<T> {//返回码private Integer code;//返回消息private String message;//返回数据private T data;public Result() {}private static <T> Result<T> build(T data) {Result<T> result = new Result<>();if (data != null)result.setData(data);return result;}public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {Result<T> result = build(body);result.setCode(resultCodeEnum.getCode());result.setMessage(resultCodeEnum.getMessage());return result;}public static <T> Result<T> fail(Integer code, String message) {Result<T> result = build(null);result.setCode(code);result.setMessage(message);return result;}public static <T> Result<T> ok(T data) {return build(data, ResultCodeEnum.SUCCESS);}public static <T> Result<T> ok() {return Result.ok(null);}public static <T> Result<T> fail() {return build(null, ResultCodeEnum.FAIL);}
}
支付接口
我这里是多订单的,如果是但订单的话这里 payUrl = zfbPayConfig.pay(goodsOrderInfoArrayList.get(0)); 可以传入一个 orderInfo 方便zfbPayConfig.pay() 获取 tradeNo 和 totalAmount。
@Operation(summary = "支付商品订单")@GetMapping(value = "/pay/zfb")@ResponseBodypublic Result pay(@RequestParam String id) throws AlipayApiException {List<String> split = List.of(id.split(","));String payUrl = "";// 单个商品的情况if (split.size() == 1) {GoodsOrderInfo orderInfo = service.getById(id);orderInfo.setTotalPrice((orderInfo.getTotalPrice()));payUrl = zfbPayConfig.pay(orderInfo);orderInfo.setCodeUrl(payUrl);orderInfo.setOrderIds(id);service.saveOrUpdate(orderInfo);}// 多个商品else {ArrayList<GoodsOrderInfo> goodsOrderInfoArrayList = new ArrayList<>();BigDecimal price = new BigDecimal("0.00");for (String shopInfoId : split) {GoodsOrderInfo orderInfo = service.getById(shopInfoId);goodsOrderInfoArrayList.add(orderInfo);price = price.add(orderInfo.getPrice().multiply(BigDecimal.valueOf(orderInfo.getGoodNum())));}System.out.println(price);goodsOrderInfoArrayList.get(0).setTotalPrice(price);payUrl = zfbPayConfig.pay(goodsOrderInfoArrayList.get(0));goodsOrderInfoArrayList.get(0).setCodeUrl(payUrl);goodsOrderInfoArrayList.get(0).setOrderIds(id);service.saveOrUpdate(goodsOrderInfoArrayList.get(0));}return Result.ok(payUrl);}
2.3.4 支付回调处理接口(用于支付成功后,支付宝通知我们支付信息)
@PostMapping("/zfb-pay/notify") // 注意这里必须是POST接口public String payNotify(HttpServletRequest request) {Map<String, String> params = new HashMap<>();Map<String, String[]> requestParams = request.getParameterMap();System.out.println("=========支付宝异步回调========");System.out.println(JSON.toJSONString(requestParams));for (String name : requestParams.keySet()) {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);}try {boolean flag = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType());if (flag) {System.out.println("支付回调信息:" + params);// TODO 验签成功System.out.println("支付成功,异步验签成功!");// 验证订单是否为当前订单String orderNumber = params.get("out_trade_no");LambdaQueryWrapper<GoodsOrderInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(GoodsOrderInfo::getOrderNo, orderNumber);
// .eq(ShopOrderInfo::getIsDeleted, 0)GoodsOrderInfo orderInfo = service.getOne(queryWrapper);if (orderInfo == null) {throw new LeaseException(ResultCodeEnum.SHOP_ORDER_NOT_FIND_ERROR);}// 验证订单金额是否正确 TODO 测试环境不验证金额都是 0.01
// BigDecimal totalPrice = new BigDecimal(params.get("total_amount"));
// BigDecimal totalPriceParam = orderInfo.getTotalPrice();
// if (!Objects.equals(totalPriceParam, totalPrice)) {
// throw new LeaseException(ResultCodeEnum.SHOP_ORDER_TOTAL_PRICE_ERROR);
// }// 验证商家ID是否一致 防止付给别人了String sellerId = config.getProperty("alipay.sellerId");String sellerIdParams = params.get("seller_id");if (!Objects.equals(sellerId, sellerIdParams)) {throw new LeaseException(ResultCodeEnum.SHOP_ORDER_BUSINESS_PID_ERROR);}// 验证APPID是否一致String appId = config.getProperty("alipay.app_id");String appIdParams = params.get("app_id");if (!Objects.equals(appIdParams, appId)) {throw new LeaseException(ResultCodeEnum.SHOP_ORDER_BUSINESS_APPID_ERROR);}// 检查交易状态String tradeStatus = params.get("trade_status");if (!"TRADE_SUCCESS".equals(tradeStatus) && !"TRADE_CLOSED".equals(tradeStatus)) {throw new LeaseException(ResultCodeEnum.SHOP_ORDER_PAY_ERROR);}// 支付成功if ("TRADE_SUCCESS".equals(tradeStatus)) {// TODO 处理订单业务 修改订单状态 记录支付日志service.processOrder(params);}// 退款操作if ("TRADE_CLOSED".equals(tradeStatus)) {String orderNo = params.get("out_biz_no");service.refund(orderNo);}return "success";} else {// TODO 支付失败标记为未付款String orderNumber = params.get("out_trade_no");service.noPay(orderNumber);return "error";}} catch (AlipayApiException e) {System.out.println("支付宝错误回调:" + e.getErrMsg());e.printStackTrace();return "error";}}
2.3.4 退款接口
ApplyRefund 退款参数
@Data
public class ApplyRefund {private String id;private String refundInfo;private String remarks;
}
退款接口
@Operation(summary = "申请退款")@PostMapping("apply/refund")public Result applyRefund(@RequestBody ApplyRefund applyRefund) {service.applyRefund(applyRefund);return Result.ok();}
3 前端代码
3.1 支付接口调用
// 支付订单
export const payShopOrderApi = (id) => {return http.get(`/app/shop/order/pay/zfb?id=${id}`)
}
3.2 支付方法
const toPayFun = async () => {// 调起支付let res = await payShopOrderApi(ids.value);console.log(res.data);uni.requestPayment({//服务提供商 通过uni.getProvider获取provider: 'alipay',//后台返回的订单数据orderInfo: res.data,//调用成功的回调success(res) {uni.showToast({title: "支付成功"})setTimeout(() => {uni.redirectTo({url: "/pages/src/user/user-order/user-order"})}, 500)},//调用失败的回调fail(err) {uni.showToast({title: "支付取消",icon: "none"})setTimeout(() => {uni.redirectTo({url: "/pages/src/user/user-order/user-order"})}, 500)}})}
3.2 退款接口调用
这里我的退款接口放在商家平台,app只负责申请退款,需要商家审核,使用的是TS代码。
// 退款
export interface PostInterfacesRefundRes {orderIds: number,tradeNo: number
}
const openDialog = async (rowData: PostInterfacesRefundRes = {} as PostInterfacesRefundRes,
) => {console.log(rowData);const params = {orderIds: rowData.orderIds,tradeNo: rowData.tradeNo}let res = await postAgreementRefundApi(params);ElMessage.success({ message: `操作成功!` })proTable.value?.getTableList()
}