阿里环球Antom支付对接
线上文档地址:
GitHub:https://github.com/alipay/global-open-sdk-java
文档:https://global.alipay.com/docs/ac/ams_zh-cn/session_cashier
maven:
<!--阿里国际支付--><dependency><groupId>com.alipay.global.sdk</groupId><artifactId>global-open-sdk-java</artifactId><version>2.0.49</version></dependency>
新增作用在实现类上的注解
import com.ycwl.blindBox.enums.PayTypeEnum;import java.lang.annotation.*;/*** <p>支付方式策略类型</p>** @Description*/
//作用在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//子类可以继承此注解
@Inherited
public @interface PayType {/*** 支付方式策略类型** @return 策略类型*/PayTypeEnum type();}
编写配置实体:AntomConfig
import lombok.Data;/*** <p>DESC: </p>* <p>VERSION:1.0.0</p>*/
@Data
public class AntomConfig {/*** 支付网关地址*/private String gatewayUrl;/*** 商户私钥*/private String merchantPrivateKey;/*** 支付宝公钥*/private String alipayPublicKey;/*** 客户端 ID*/private String clientId;/*** 系统商户 ID*/private String referenceMerchantId;/*** 支付回调地址*/private String payNotifyUrl;/*** 退款回调地址*/private String refundNotifyUrl;/*** 货币代码*/private String currency;/*** 最小币种单位*/private String currencyValue;}
编写服务类:AlipayService
import com.alipay.global.api.exception.AlipayApiException;
import com.alipay.global.api.request.ams.notify.AlipayPayResultNotify;
import com.alipay.global.api.request.ams.notify.AlipayRefundNotify;
import com.ycwl.blindBox.constant.baseResp.ApiResponse;
import com.ycwl.blindBox.model.common.AntomResultResp;
import com.ycwl.blindBox.model.common.StatusReqDTO;
import com.ycwl.blindBox.model.dto.BagBoxDeliverDTO;
import com.ycwl.blindBox.model.dto.BagBoxExchangeDTO;
import com.ycwl.blindBox.model.dto.BagBoxTrailDTO;
import com.ycwl.blindBox.model.dto.BagBoxTrailGruopGoodsDTO;
import com.ycwl.blindBox.model.dto.giveRecord.GiveAppReqDTO;
import com.ycwl.blindBox.model.vo.*;
import org.springframework.web.bind.annotation.RequestBody;import java.math.BigDecimal;
import java.util.List;
import java.util.Set;/*** @author songmingsong* @description antom支付*/
public interface AlipayService {/*** 创建支付订单** @param paymentRequestId 商户为标识支付请求所分配的专属 ID。Antom 使用此字段进行幂等性控制。* @param orderId 在商户端标识订单的专属 ID,由直接向客户提供服务或商品的商户分配* @param orderDescription 订单的摘要描述。此字段用于显示用户消费记录以及其他后续操作* @param redirectUrl 用户完成支付后跳转到的商户页面链接。* @return* @throws AlipayApiException*/AntomPayResultVO createPayment(String paymentRequestId,String orderId,String orderDescription,String redirectUrl,BigDecimal amount) throws AlipayApiException;/*** 支付结果查询** @param payOrderNo 支付订单号* @return* @throws AlipayApiException*/AntomPayResultVO paymentResultInquiry(String payOrderNo) throws AlipayApiException;/*** 取消支付** @param payOrderNo 支付订单号* @return* @throws AlipayApiException*/AntomPayResultVO paymentCancel(String payOrderNo) throws AlipayApiException;/*** 支付回调** @param alipayPayResultNotify*/AntomResultResp payNotify(AlipayPayResultNotify alipayPayResultNotify) throws AlipayApiException;/*** 退款** @param refundVO*/AntomRefundResultVO refund(AntomRefundVO refundVO) throws AlipayApiException;/*** 退款结果查询** @param payOrderNo 支付订单号* @return* @throws AlipayApiException*/AntomRefundResultVO refundInquiry(String payOrderNo) throws AlipayApiException;/*** 退款回调** @param alipayRefundNotify*/AntomResultResp refundNotify(AlipayRefundNotify alipayRefundNotify);
}
服务实现类:AlipayServiceImpl
这里面包含了一些业务逻辑,不需要的话请删除
package com.ycwl.blindBox.service.impl;import com.alibaba.fastjson2.JSONObject;
import com.alipay.global.api.AlipayClient;
import com.alipay.global.api.DefaultAlipayClient;
import com.alipay.global.api.exception.AlipayApiException;
import com.alipay.global.api.model.Result;
import com.alipay.global.api.model.ResultStatusType;
import com.alipay.global.api.model.ams.*;
import com.alipay.global.api.model.ams.Order;
import com.alipay.global.api.request.ams.notify.AlipayPayResultNotify;
import com.alipay.global.api.request.ams.notify.AlipayRefundNotify;
import com.alipay.global.api.request.ams.pay.*;
import com.alipay.global.api.response.ams.pay.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ycwl.blindBox.annotation.PayType;
import com.ycwl.blindBox.config.ApplicationContextHelper;
import com.ycwl.blindBox.constant.OrderNoTypeConstant;
import com.ycwl.blindBox.constant.SystemSettingTypeConstant;
import com.ycwl.blindBox.enums.*;
import com.ycwl.blindBox.exception.BaseException;
import com.ycwl.blindBox.mapper.*;
import com.ycwl.blindBox.model.common.AntomConfig;
import com.ycwl.blindBox.model.common.AntomResultResp;
import com.ycwl.blindBox.model.dto.OrderParamDTO;
import com.ycwl.blindBox.model.dto.vipCard.VipCardAwardSetDTO;
import com.ycwl.blindBox.model.entity.*;
import com.ycwl.blindBox.model.vo.AntomPayResultVO;
import com.ycwl.blindBox.model.vo.AntomRefundResultVO;
import com.ycwl.blindBox.model.vo.AntomRefundVO;
import com.ycwl.blindBox.model.vo.PayParamVO;
import com.ycwl.blindBox.service.*;
import com.ycwl.blindBox.utils.capital.CapitalChangeUtil;
import com.ycwl.blindBox.utils.dateUtils.DateUtils;
import com.ycwl.blindBox.utils.pubfunc.PubfuncUtil;
import com.ycwl.blindBox.utils.snowFlakeUtils.SnowFlakeUtil;
import com.ycwl.blindBox.utils.userLevelUtils.UserLevelUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;import static com.ycwl.blindBox.constant.SystemSettingTypeConstant.ANTOM;
import static com.ycwl.blindBox.enums.BizCodeEnum.*;
import static com.ycwl.blindBox.enums.SysSettingEnum.CZ_INTEGRAL;
import static com.ycwl.blindBox.enums.SysSettingEnum.GZ_INTEGRAL;/*** @Author: songmingsong* @CreateTime: 2024-12-25* @Description: 阿里Antom全球支付实现* @Version: 1.0*/
@PayType(type = PayTypeEnum.ONLINE)
@Component("AlipayServiceImpl")
@Slf4j
public class AlipayServiceImpl implements PayProcessingService, AlipayService {@Autowiredprivate RefundRecordLogMapper refundRecordLogMapper;@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate OrderExpressMapper orderExpressMapper;@Autowiredprivate UserVipCardOrderMapper userVipCardOrderMapper;@Autowiredprivate BlindBoxOrderServiceImpl blindBoxOrderService;@Autowiredprivate UserVipCardMapper userVipCardMapper;@Autowiredprivate VipCardAwardSetMapper awardSetMapper;@Autowiredprivate VipCardAwardMapper awardMapper;@Autowiredprivate UserCardAwardReceiveRecordMapper userCardAwardReceiveRecordMapper;@Autowiredprivate VipCardMapper vipCardMapper;@Autowiredprivate UserBalanceRechargeLogMapper userBalanceRechargeLogMapper;@Autowiredprivate OrderService orderService;@Autowiredprivate CapitalChangeUtil capitalChangeUtil;@Autowiredprivate UserBalanceChangeLogMapper userBalanceChangeLogMapper;@Autowiredprivate UserLevelUtil userLevelUtil;@Autowiredprivate RechargeSettingMapper rechargeSettingMapper;@Autowiredprivate BagServiceImpl bagService;@Autowiredprivate ShopOrderServiceImpl shopOrderService;@Autowiredprivate InviteNewRecordService inviteNewRecordService;private final Map<String, Lock> lockMap = new ConcurrentHashMap<>();private AntomConfig antomConfig = new AntomConfig();private String GATEWAY_URL;private String MERCHANT_PRIVATE_KEY;private String ALIPAY_PUBLIC_KEY;private String CLIENT_ID;private String REFERENCE_MERCHANT_ID; // 用于标识直接向客户提供服务或商品的商户 ID。private String PAY_NOTIFY_URL; // 支付回调地址private String REFUND_NOTIFY_URL; // 退款回调地址// 查询地址:https://www.iban.hk/currency-codesprivate String CURRENCY_VND = "VND"; // 默认越南盾代码// 查询地址:https://docs.antom.com/ac/ref_zh-cn/cc#ONkIeprivate BigDecimal VALUE_VND = BigDecimal.ONE; // 默认越南盾最小币种单位private final AlipayClient client;public AlipayServiceImpl() {getAntomConfig();this.GATEWAY_URL = antomConfig.getGatewayUrl();this.MERCHANT_PRIVATE_KEY = antomConfig.getMerchantPrivateKey();this.ALIPAY_PUBLIC_KEY = antomConfig.getAlipayPublicKey();this.CLIENT_ID = antomConfig.getClientId();this.REFERENCE_MERCHANT_ID = antomConfig.getReferenceMerchantId();this.PAY_NOTIFY_URL = antomConfig.getPayNotifyUrl();this.REFUND_NOTIFY_URL = antomConfig.getRefundNotifyUrl();this.CURRENCY_VND = antomConfig.getCurrency();this.VALUE_VND = BigDecimal.valueOf(Integer.parseInt(antomConfig.getCurrencyValue()));this.client = new DefaultAlipayClient(GATEWAY_URL, MERCHANT_PRIVATE_KEY, ALIPAY_PUBLIC_KEY, CLIENT_ID);}public AntomConfig getAntomConfig() {List<SysSetting> data = PubfuncUtil.getSdParam(ANTOM);data.forEach(setting -> {String key = setting.getKey();String value = setting.getValue();// 根据枚举匹配对应的方法调用AntomConfigKeyEnum.fromKey(key).ifPresent(action -> action.apply(antomConfig, value));});return antomConfig;}@Overridepublic JSONObject pay(PayParamVO payParamVO) throws AlipayApiException {orderService.checkStatus(payParamVO.getOrderNo());String payOrderNo = payParamVO.getPayOrderNo();if (payOrderNo.isEmpty()) {throw new BaseException(PAY_NO_EMPTY);}JSONObject rtnJson = new JSONObject();AntomPayResultVO payment = createPayment(payOrderNo, payParamVO.getOrderNo(), payParamVO.getNotes(), payParamVO.getRedirectUrl(), payParamVO.getPayPrice());log.info("支付结果:{}", payment);// 更新第三方单号String paymentId = payment.getPaymentId();String orderNo = payParamVO.getOrderNo();Boolean success = payment.getSuccess();if (payment.getSuccess()) {// 支付收银台创建成功,返回链接rtnJson.put("url", payment.getNormalUrl());} else {throw new BaseException(PAYMENT_FAILED);}return rtnJson;}@Overridepublic JSONObject refund(RefundRecordLog refundRecordLog) throws AlipayApiException {AntomRefundVO antomRefundVO = new AntomRefundVO();antomRefundVO.setRefundRequestId(refundRecordLog.getOrderNo()); // 订单号antomRefundVO.setReferenceRefundId(refundRecordLog.getOrderId().toString());antomRefundVO.setRefundAmount(refundRecordLog.getRefundAmount());refund(antomRefundVO);return null;}@Overridepublic AntomPayResultVO paymentResultInquiry(String payOrderNo) throws AlipayApiException {AlipayPayQueryRequest alipayPayQueryRequest = new AlipayPayQueryRequest();alipayPayQueryRequest.setMerchantAccountId(REFERENCE_MERCHANT_ID);alipayPayQueryRequest.setClientId(CLIENT_ID);alipayPayQueryRequest.setPaymentRequestId(payOrderNo);// TODO 沙箱地址
// alipayPayQueryRequest.setPath("/ams/sandbox/api/v1/payments/inquiryPayment");AntomPayResultVO antomPayResultVO = new AntomPayResultVO();AlipayPayQueryResponse execute = client.execute(alipayPayQueryRequest);String orderNo = execute.getPaymentRequestId();String paymentId = execute.getPaymentId();String paymentTime = execute.getPaymentTime(); // 支付时间Result result = execute.getResult();String resultMessage = result.getResultMessage();ResultStatusType resultStatus = result.getResultStatus();if (resultStatus == ResultStatusType.F) {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), false);} else if (resultStatus == ResultStatusType.U) {// 需要重试return null;} else {// 成功TransactionStatusType paymentStatus = execute.getPaymentStatus();if (paymentStatus.equals(TransactionStatusType.SUCCESS)) { // 成功antomPayResultVO.setSuccess(true);antomPayResultVO.setTime(parseTime(paymentTime));payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), true);} else if (paymentStatus.equals(TransactionStatusType.FAIL)) { // 失败antomPayResultVO.setSuccess(false);payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), false);}}return antomPayResultVO;}@Overridepublic AntomPayResultVO paymentCancel(String payOrderNo) throws AlipayApiException {AlipayPayCancelRequest alipayPayCancelRequest = new AlipayPayCancelRequest();alipayPayCancelRequest.setMerchantAccountId(REFERENCE_MERCHANT_ID);alipayPayCancelRequest.setClientId(CLIENT_ID);alipayPayCancelRequest.setPaymentRequestId(payOrderNo);AntomPayResultVO antomPayResultVO = new AntomPayResultVO();AlipayPayCancelResponse execute = client.execute(alipayPayCancelRequest);Result result = execute.getResult();ResultStatusType resultStatus = result.getResultStatus();if (resultStatus == ResultStatusType.F) {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());} else if (resultStatus == ResultStatusType.U) {// 需要重试return this.paymentCancel(payOrderNo);} else {// 成功String paymentTime = execute.getCancelTime();antomPayResultVO.setSuccess(true);antomPayResultVO.setTime(parseTime(paymentTime));}return antomPayResultVO;}@Overridepublic AntomResultResp payNotify(AlipayPayResultNotify alipayPayResultNotify) throws AlipayApiException {/** PAYMENT_RESULT: 表示通知是关于支付结果的。* PAYMENT_PENDING: 表示用户已完成支付。商家需要等待最终的支付结果* */String notifyType = alipayPayResultNotify.getNotifyType();Result result = alipayPayResultNotify.getResult();String orderNo = alipayPayResultNotify.getPaymentRequestId(); // 订单号String paymentId = alipayPayResultNotify.getPaymentId();String paymentTime = alipayPayResultNotify.getPaymentTime(); // 支付时间/** S: 当 notifyType 为PAYMENT_RESULT时,表示支付成功;当 notifyType 为PAYMENT_PENDING时,表示支付处理中。* F: 表示支付失败。* */ResultStatusType resultStatus = result.getResultStatus();String resultMessage = result.getResultMessage();if (resultStatus == ResultStatusType.S && "PAYMENT_RESULT".equals(notifyType)) {payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), true);} else if (resultStatus == ResultStatusType.S && "PAYMENT_PENDING".equals(notifyType)) {// 支付处理中,单开线程查询结果new Thread(() -> {ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);AtomicInteger attemptCounter = new AtomicInteger(0);int maxAttempts = 20; // 最多执行 20 次long delayInSeconds = 10; // 每隔 10 秒执行一次scheduler.scheduleAtFixedRate(() -> {try {int currentAttempt = attemptCounter.incrementAndGet();if (currentAttempt > maxAttempts) {scheduler.shutdown(); // 超过最大次数,停止任务return;}AntomPayResultVO antomPayResultVO = paymentResultInquiry(orderNo);if (!Objects.isNull(antomPayResultVO)) {Boolean success = antomPayResultVO.getSuccess();payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), success);scheduler.shutdown(); // 停止任务}} catch (AlipayApiException e) {scheduler.shutdown(); // 出现异常时停止任务throw new RuntimeException(e);}}, 0, delayInSeconds, TimeUnit.SECONDS);}).start();} else if (resultStatus == ResultStatusType.F) {payUpdateStatus(orderNo, paymentId, resultMessage, parseTime(paymentTime), false);}return new AntomResultResp();}@Overridepublic AntomRefundResultVO refund(AntomRefundVO refundVO) throws AlipayApiException {AlipayRefundRequest alipayRefundRequest = new AlipayRefundRequest();alipayRefundRequest.setClientId(CLIENT_ID);Amount refundAmount = new Amount();refundAmount.setCurrency(CURRENCY_VND);refundAmount.setValue(refundVO.getRefundAmount().multiply(VALUE_VND).toString());alipayRefundRequest.setRefundAmount(refundAmount);alipayRefundRequest.setRefundRequestId(refundVO.getRefundRequestId());alipayRefundRequest.setReferenceRefundId(refundVO.getReferenceRefundId());alipayRefundRequest.setRefundNotifyUrl(REFUND_NOTIFY_URL); // 用于接收支付结果通知的链接。// TODO 沙箱地址
// alipayRefundRequest.setPath("/ams/sandbox/api/v1/payments/refund");AlipayRefundResponse execute = client.execute(alipayRefundRequest);Result result = execute.getResult();ResultStatusType resultStatus = result.getResultStatus();AntomRefundResultVO resultVO = new AntomRefundResultVO();if (resultStatus == ResultStatusType.F) {resultVO.setSuccess(false);resultVO.setMsg(result.getResultMessage());} else if (resultStatus == ResultStatusType.U) {/**结果未知。可能由于系统或网络问题导致处理失败。使用相同的 refundRequestId 重试,或使用 退款查询 接口查询退款结果* */this.refund(refundVO);} else {// 成功String refundTime = execute.getRefundTime();resultVO.setSuccess(true);resultVO.setTime(parseTime((refundTime)));resultVO.setReferenceRefundId(execute.getAcquirerReferenceNo());refundVO.setRefundRequestId(execute.getRefundRequestId());}return resultVO;}@Overridepublic AntomRefundResultVO refundInquiry(String payOrderNo) throws AlipayApiException {AlipayInquiryRefundRequest alipayInquiryRefundRequest = new AlipayInquiryRefundRequest();alipayInquiryRefundRequest.setMerchantAccountId(REFERENCE_MERCHANT_ID);alipayInquiryRefundRequest.setClientId(CLIENT_ID);alipayInquiryRefundRequest.setRefundRequestId(payOrderNo);AntomRefundResultVO resultVO = new AntomRefundResultVO();AlipayInquiryRefundResponse execute = client.execute(alipayInquiryRefundRequest);Result result = execute.getResult();ResultStatusType resultStatus = result.getResultStatus();if (resultStatus == ResultStatusType.F) {resultVO.setSuccess(false);resultVO.setMsg(result.getResultMessage());} else if (resultStatus == ResultStatusType.U) {// 需要重试return this.refundInquiry(payOrderNo);} else if (resultStatus == ResultStatusType.S) {// 成功TransactionStatusType refundStatus = execute.getRefundStatus();if (refundStatus.equals(TransactionStatusType.SUCCESS)) { // 成功String time = execute.getRefundTime();resultVO.setSuccess(true);resultVO.setTime(parseTime(time));} else if (refundStatus.equals(TransactionStatusType.FAIL)) { // 失败resultVO.setSuccess(false);} else if (refundStatus.equals(TransactionStatusType.PROCESSING)) { // 退款处理中resultVO.setSuccess(false);resultVO.setMsg("退款处理中");}}return resultVO;}@Override@Transactionalpublic AntomResultResp refundNotify(AlipayRefundNotify alipayRefundNotify) {/** REFUND_RESULT:表示是关于退款结果的通知* */String notifyType = alipayRefundNotify.getNotifyType();Result result = alipayRefundNotify.getResult();String resultCode = result.getResultCode();Amount refundAmount = alipayRefundNotify.getRefundAmount();String value = refundAmount.getValue(); // 退款金额String paymentRequestId = alipayRefundNotify.getRefundRequestId(); // 订单号String time = alipayRefundNotify.getRefundTime(); // 退款时间/** S: 当 notifyType 为PAYMENT_RESULT时,表示支付成功;当 notifyType 为PAYMENT_PENDING时,表示支付处理中。* F: 表示支付失败。* */ResultStatusType resultStatus = result.getResultStatus();RefundRecordLog refundRecord = new RefundRecordLog();refundRecord.setRefundTime(parseTime(time));if (resultStatus == ResultStatusType.S && "REFUND_RESULT".equals(notifyType)) {// 成功refundRecord.setStatus(1); // 退款状态 1成功 2失败 3退款中refundUpdateStatus(paymentRequestId, value);} else if (resultStatus == ResultStatusType.F) {// 失败refundRecord.setStatus(2); // 退款状态 1成功 2失败 3退款中orderMapper.updatePayStatus(paymentRequestId, 8);}// 更新退款记录refundRecordLogMapper.update(refundRecord, new LambdaQueryWrapper<RefundRecordLog>().eq(RefundRecordLog::getOrderNo, paymentRequestId));// 修改订单状态return new AntomResultResp();}@Overridepublic AntomPayResultVO createPayment(String paymentRequestId,String orderId,String orderDescription,String redirectUrl,BigDecimal amount) throws AlipayApiException {AlipayPayRequest alipayPayRequest = new AlipayPayRequest();alipayPayRequest.setClientId(CLIENT_ID);alipayPayRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);alipayPayRequest.setPaymentRequestId(paymentRequestId); // 商户为标识支付请求所分配的专属 ID。Antom 使用此字段进行幂等性控制。alipayPayRequest.setPath("ams/api/v1/payments/pay");// TODO 沙箱地址
// alipayPayRequest.setPath("/ams/sandbox/api/v1/payments/pay");// 商家在订单币种中请求接收的支付金额Amount paymentAmount = new Amount();paymentAmount.setCurrency(CURRENCY_VND); // 币种代码paymentAmount.setValue(amount.multiply(VALUE_VND).setScale(0, RoundingMode.UP).toString()); // 以最小币种单位的正整数形式表示的金额值alipayPayRequest.setPaymentAmount(paymentAmount);// 包括买家、商户、商品、金额、配送信息和购买环境的订单信息Order order = new Order();order.setReferenceOrderId(orderId); // 在商户端标识订单的专属 ID,由直接向客户提供服务或商品的商户分配order.setOrderDescription(orderDescription); // 订单的摘要描述。此字段用于显示用户消费记录以及其他后续操作。// Set merchant infoMerchant merchant = new Merchant();merchant.setReferenceMerchantId(REFERENCE_MERCHANT_ID);order.setMerchant(merchant);// order中请求接收的支付金额Amount orderAmount = new Amount();orderAmount.setCurrency(CURRENCY_VND);orderAmount.setValue(amount.multiply(VALUE_VND).setScale(0, RoundingMode.UP).toString());order.setOrderAmount(orderAmount);alipayPayRequest.setOrder(order);// 有关下单环境的信息,例如设备信息Env env = new Env();/** terminalType String REQUIRED*商户服务适用的终端类型。有效值为:*WEB: 客户端终端类型为网站,通过 PC 浏览器打开。*WAP: 客户端终端类型为 H5 页面,通过移动浏览器打开。*APP: 客户端终端类型为移动应用。*MINI_APP: 商户端的终端类型为手机小程序。** */env.setTerminalType(TerminalType.APP);/**操作系统类型。有效值包括:*IOS: 表示操作系统是苹果的 iOS。*ANDROID: 表示操作系统是谷歌的 Android。**/env.setOsType(OsType.IOS);alipayPayRequest.setEnv(env);// 商户或收单机构用于收款的支付方式。PaymentMethod paymentMethod = new PaymentMethod();// 支付方式选项中包含的支付方式类型: https://global.alipay.com/docs/ac/pm_zh-cn/enumeration_valuespaymentMethod.setPaymentMethodType(WalletPaymentMethodType.ZALOPAY.name());alipayPayRequest.setPaymentMethod(paymentMethod);// 回调地址alipayPayRequest.setPaymentNotifyUrl(PAY_NOTIFY_URL); // 用于接收支付结果通知的链接。alipayPayRequest.setPaymentRedirectUrl(redirectUrl); // 用户完成支付后跳转到的商户页面链接。// 支付请求的结算策略SettlementStrategy settlementStrategy = new SettlementStrategy();settlementStrategy.setSettlementCurrency(CURRENCY_VND); // 商户期望结算币种的 ISO 币种代码alipayPayRequest.setSettlementStrategy(settlementStrategy);AlipayPayResponse alipayPayResponse = client.execute(alipayPayRequest);String paymentId = alipayPayResponse.getPaymentId();Result result = alipayPayResponse.getResult();ResultStatusType resultStatus = result.getResultStatus();AntomPayResultVO antomPayResultVO = new AntomPayResultVO();antomPayResultVO.setPaymentId(paymentId);if (resultStatus == ResultStatusType.F) {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());} else if (resultStatus == ResultStatusType.U) {/**当返回此值时,检查结果代码:*结果代码不是 PAYMENT_IN_PROCESS: 接口调用失败。使用新的 paymentRequestId 值再次调用此接口。*结果代码是 PAYMENT_IN_PROCESS: 检查是否返回了一个或多个(appLinkUrl, normalUrl, schemeUrl)链接:*返回了一个或多个链接: 交易创建成功。将用户重定向到链接指定的地址以完成支付。*没有返回链接: 交易创建失败。使用新的 paymentRequestId 值再次调用 支付(收银台) 接口。如果问题持续存在,请联系 Antom 技术支持。* */if (!result.getResultCode().equals("PAYMENT_IN_PROCESS")) {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());} else {String normalUrl = alipayPayResponse.getNormalUrl();if (StringUtils.isNotBlank(normalUrl)) {antomPayResultVO.setSuccess(true);antomPayResultVO.setNormalUrl(normalUrl);} else {antomPayResultVO.setSuccess(false);antomPayResultVO.setMsg(result.getResultMessage());}}} else {// 成功String normalUrl = alipayPayResponse.getNormalUrl(); // 将用户跳转到默认浏览器或嵌入式 WebView 中的 WAP 或 WEB 页面的链接。antomPayResultVO.setSuccess(true);antomPayResultVO.setNormalUrl(normalUrl);}return antomPayResultVO;}/*** 退款-修改对应订单状态** @param orderNo* @param value 退款金额*/private void refundUpdateStatus(String orderNo, String value) {BigDecimal refundAmount = BigDecimal.valueOf(Integer.parseInt(value));if (orderNo.contains(OrderNoTypeConstant.ORDER)) {com.ycwl.blindBox.model.entity.Order orderDB = orderMapper.selectOne(new LambdaQueryWrapper<com.ycwl.blindBox.model.entity.Order>().eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo));com.ycwl.blindBox.model.entity.Order order = new com.ycwl.blindBox.model.entity.Order();BigDecimal payPrice = orderDB.getPayPrice();if (refundAmount.compareTo(payPrice) <= 0) {order.setPayStatus(4);order.setStatus(2);} else {order.setPayStatus(3);order.setStatus(7);}orderMapper.update(order, new LambdaUpdateWrapper<com.ycwl.blindBox.model.entity.Order>().eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo));} else if (orderNo.contains(OrderNoTypeConstant.EXPRESS)) {OrderExpress orderExpressDB = orderExpressMapper.selectOne(new LambdaQueryWrapper<OrderExpress>().eq(OrderExpress::getOrderNo, orderNo));BigDecimal amount = orderExpressDB.getAmount();OrderExpress orderExpress = new OrderExpress();if (refundAmount.compareTo(amount) <= 0) {orderExpress.setPayStatus(4);} else {orderExpress.setPayStatus(3);}orderExpressMapper.update(orderExpress, new LambdaUpdateWrapper<OrderExpress>().eq(OrderExpress::getOrderNo, orderNo));} else if (orderNo.contains(OrderNoTypeConstant.VIP)) {UserVipCardOrder userVipCardOrderDB = userVipCardOrderMapper.selectOne(new LambdaQueryWrapper<UserVipCardOrder>().eq(UserVipCardOrder::getOrderNo, orderNo));BigDecimal price = userVipCardOrderDB.getPrice();UserVipCardOrder userVipCardOrder = new UserVipCardOrder();if (refundAmount.compareTo(price) <= 0) {userVipCardOrder.setPayStatus(4);} else {userVipCardOrder.setPayStatus(3);}userVipCardOrderMapper.update(userVipCardOrder, new LambdaUpdateWrapper<UserVipCardOrder>().eq(UserVipCardOrder::getOrderNo, orderNo));}}/*** 支付-修改对应订单状态** @param orderNo* @param paymentId* @param rtnMsg* @param time* @param success*/private void payUpdateStatus(String orderNo, String paymentId, String rtnMsg, Date time, Boolean success) throws AlipayApiException {if (orderNo.contains(OrderNoTypeConstant.ORDER)) {// order表com.ycwl.blindBox.model.entity.Order order = new com.ycwl.blindBox.model.entity.Order();order.setThirdCode(paymentId);order.setReturnMsg(rtnMsg);order.setPayTime(time);order.setPayStatus(2);order.setStatus(2);if (!success) {order.setPayStatus(6);orderMapper.update(order, new LambdaUpdateWrapper<com.ycwl.blindBox.model.entity.Order>().eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo));} else {// 验证订单信息com.ycwl.blindBox.model.entity.Order orderDB = orderMapper.selectOne(new LambdaQueryWrapper<com.ycwl.blindBox.model.entity.Order>().eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo));Integer type = orderDB.getType();if (type == 2) {// 盲盒订单OrderParamDTO orderParamDTO = new OrderParamDTO();orderParamDTO.setId(order.getId());orderParamDTO.setTotalNum(order.getTotalNum());orderParamDTO.setUnitPrice(order.getUnitPrice());orderParamDTO.setOrderNo(order.getOrderNo());orderParamDTO.setTraceId(0);orderParamDTO.setBoxId(order.getBoxId());orderParamDTO.setBlindboxId(order.getBlindboxId());orderParamDTO.setUserId(order.getUserId());orderParamDTO.setUserName(order.getUserName());orderParamDTO.setPayPrice(order.getPayPrice());orderParamDTO.setPayIntegral(order.getPayIntegral());orderParamDTO.setPlayId(order.getPlayId());orderParamDTO.setPayTime(time);orderParamDTO.setPayOrderNo(order.getPayOrderNo());orderParamDTO.setNotes("盲盒购买" + order.getTotalNum() + "个");Integer payWay = order.getPayWay();PayTypeEnum payTypeEnum = PayTypeEnum.getByType(payWay);orderParamDTO.setPayTypeEnum(payTypeEnum);if (payTypeEnum == PayTypeEnum.ONLINE) {blindBoxOrderService.completeOrder(orderParamDTO);}} else {// 普通订单
// orderMapper.update(order, new LambdaUpdateWrapper<com.ycwl.blindBox.model.entity.Order>()
// .eq(com.ycwl.blindBox.model.entity.Order::getOrderNo, orderNo)
// .eq(com.ycwl.blindBox.model.entity.Order::getPayStatus, 1));shopOrderService.completeOrder(order);}}} else if (orderNo.contains(OrderNoTypeConstant.EXPRESS)) {// 运费表OrderExpress orderExpress = orderExpressMapper.selectOne(new LambdaQueryWrapper<OrderExpress>().eq(OrderExpress::getOrderNo, orderNo));if (orderExpress.getPayStatus() == 1) {orderExpress.setThirdNo(paymentId);orderExpress.setThirdReturn(paymentId);orderExpress.setPayTime(time);orderExpress.setPayStatus(2);if (!success) {orderExpress.setPayStatus(6);orderExpress.setPayError(rtnMsg);orderExpressMapper.updateById(orderExpress);} else {bagService.completeExpressOrder(orderExpress);}}} else if (orderNo.contains(OrderNoTypeConstant.VIP)) {// 会员卡表// 加锁,防止主动查询和回调同时触发以下逻辑Lock lock = lockMap.computeIfAbsent(orderNo, k -> new ReentrantLock());lock.lock();try {UserVipCardOrder order = userVipCardOrderMapper.getByOrderNo(orderNo);order.setReturnMsg(rtnMsg);order.setExternalOrderId(paymentId);// 支付状态 0待支付 1已支付 2已取消Integer payStatus = order.getPayStatus();if (payStatus == 1 || payStatus == 2) {vipCardPaymentCallback(order, success);log.info("会员卡支付结果同步成功");} else {log.error("会员卡支付结果已同步,不再重复执行");}} catch (Exception e) {log.error("会员卡支付回调异常", e);} finally {lock.unlock();lockMap.remove(orderNo);}} else if (orderNo.contains(OrderNoTypeConstant.RECHARGE)) {// 加锁,防止主动查询和回调同时触发以下逻辑Lock lock = lockMap.computeIfAbsent(orderNo, k -> new ReentrantLock());lock.lock();try {// 充值订单UserBalanceRechargeLog userBalanceRechargeLog = userBalanceRechargeLogMapper.selectOne(new LambdaQueryWrapper<UserBalanceRechargeLog>().eq(UserBalanceRechargeLog::getRechargeNo, orderNo));userBalanceRechargeLog.setThirdReturn(rtnMsg);userBalanceRechargeLog.setThirdNo(paymentId);// 充值状态 1:待支付 2:支付成功 3:支付失败 4:过期 5:取消Integer status = userBalanceRechargeLog.getStatus();if (status != 1) {rechargeSuccess(userBalanceRechargeLog, success);log.info("充值订单支付结果同步成功");} else {log.error("充值订单支付结果已同步,不再重复执行");}} catch (Exception e) {log.error("充值订单回调异常", e);} finally {lock.unlock();lockMap.remove(orderNo);}}}/*** 充值成功处理逻辑* 添加积分、添加经验、添加余额** @param userBalanceRechargeLog 充值订单* @param success 是否成功*/@Transactional(rollbackFor = Exception.class)public void rechargeSuccess(UserBalanceRechargeLog userBalanceRechargeLog, Boolean success) {if (success) {Integer userId = userBalanceRechargeLog.getUserId();Integer orderId = userBalanceRechargeLog.getId();// 充值金额BigDecimal amount = userBalanceRechargeLog.getAmount();// 增加积分capitalChangeUtil.addIntegral(amount, userId, orderId, IntegralChangeTypeEnum.RECHARGE_ADDITION);// 更新余额capitalChangeUtil.addBalance(amount, userId, orderId, BalanceChangeTypeEnum.RECHARGE_ADDITION);// 增加赠送金额RechargeSetting rechargeSetting = rechargeSettingMapper.getMaxByAmount(amount);if (rechargeSetting != null) {Integer giveRatio = rechargeSetting.getGiveRatio();BigDecimal giveAmount = amount.multiply(BigDecimal.valueOf(giveRatio)).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);capitalChangeUtil.addBalance(giveAmount, userId, orderId, BalanceChangeTypeEnum.RECHARGE_ADDITION);}// 增加用户经验、更新用户等级userLevelUtil.updateUserLevelUtil(amount, userId, PayTypeEnum.ONLINE, true);userBalanceRechargeLog.setStatus(2);userBalanceRechargeLogMapper.updateById(userBalanceRechargeLog);} else {userBalanceRechargeLog.setStatus(3);userBalanceRechargeLogMapper.updateById(userBalanceRechargeLog);}}/*** 转换时间 2019-11-27T12:01:01+08:00 转成date** @param dateTimeString* @return*/private Date parseTime(String dateTimeString) {OffsetDateTime offsetDateTime = OffsetDateTime.parse(dateTimeString, DateTimeFormatter.ISO_OFFSET_DATE_TIME);return Date.from(offsetDateTime.toInstant());}/*** 会员卡支付回调** @param order* @param success*/private void vipCardPaymentCallback(UserVipCardOrder order, Boolean success) {Integer orderId = order.getId();Integer userId = order.getUserId();if (success) {// 假设支付成功// 订单状态改为已支付order.setPayStatus(VipCardOrderStatusEnum.PAID_ALREADY.getCode());order.setPayTime(new Date());userVipCardOrderMapper.updateById(order);Long cardId = order.getCardId();// 支付成功,给用户添加会员卡,如果用户已有会员卡就修改会员卡的状态和过期日期UserVipCard userVipCard = userVipCardMapper.getUserVipCardByCardId(order.getCardId(), userId);// 本次购买的月卡,领取奖励的起始时间Date startDate = null;if (userVipCard == null) {// 用户没有该会员卡,新增用户会员卡userVipCard = new UserVipCard();userVipCard.setId(SnowFlakeUtil.getLongId());userVipCard.setCardId(order.getCardId());userVipCard.setCardType(order.getCardType());userVipCard.setCardName(order.getCardName());userVipCard.setUserId(userId);userVipCard.setStatus(UserVipCardStatusEnum.UNUSED.getCode());// 计算过期日期Date expiredDate = getExpiredDate(userVipCard.getCardType(), new Date());startDate = new Date();userVipCard.setExpiredDate(expiredDate);userVipCardMapper.insert(userVipCard);} else {// 用户已有该会员卡,修改会员卡的状态和延长过期日期Integer status = userVipCard.getStatus();Date expiredDate = null;if (status.equals(UserVipCardStatusEnum.USED.getCode())) {userVipCard.setStatus(UserVipCardStatusEnum.UNUSED.getCode());// 计算过期日期,原來的已经过期了,就在当前时间上累加expiredDate = getExpiredDate(userVipCard.getCardType(), new Date());startDate = new Date();} else {startDate = DateUtils.addDateDays(userVipCard.getExpiredDate(), 1);// 计算过期日期,原來的还没过期就再原来的时间上累加expiredDate = getExpiredDate(userVipCard.getCardType(), userVipCard.getExpiredDate());}userVipCard.setExpiredDate(expiredDate);userVipCardMapper.updateById(userVipCard);}// 添加用户会员卡奖励信息// 用户会员卡idLong userVipCardId = userVipCard.getId();// 用户可领取奖励列表List<UserCardAwardReceiveRecord> userCardAwardList = new ArrayList<>();List<VipCardAwardSetDTO> awardSetList = awardSetMapper.getByCardId(cardId);for (VipCardAwardSetDTO awardSet : awardSetList) {List<VipCardAward> cardAwards = awardMapper.getBySetId(awardSet.getId());for (VipCardAward cardAward : cardAwards) {UserCardAwardReceiveRecord receiveRecord = new UserCardAwardReceiveRecord();receiveRecord.setId(SnowFlakeUtil.getLongId());receiveRecord.setUserCardId(userVipCardId);receiveRecord.setOrderId(orderId);receiveRecord.setAwardType(cardAward.getType());receiveRecord.setCouponId(cardAward.getCouponId());receiveRecord.setCouponQuantity(cardAward.getCouponQuantity());receiveRecord.setAmount(cardAward.getAmount());receiveRecord.setUserId(userId);receiveRecord.setDay(awardSet.getDay());receiveRecord.setReceiveStatus(UserCardAwardReceiveStatusEnum.NOT_CLAIMED.getCode());if (awardSet.getDay() == 1 && startDate.compareTo(new Date()) == 0) {receiveRecord.setStatus(UserCardAwardStatusEnum.RECEIVING_IN_PROGRESS.getCode());} else {receiveRecord.setStatus(UserCardAwardStatusEnum.NOT_STARTED_YET.getCode());}// 奖励过期日期(创建日期+day)receiveRecord.setExpiredDate(DateUtils.addDateDays(startDate, awardSet.getDay()));userCardAwardList.add(receiveRecord);}}// 添加用户奖励信息userCardAwardReceiveRecordMapper.insertBatch(userCardAwardList);// 增加用户积分capitalChangeUtil.addIntegral(order.getPrice(), userId, orderId, IntegralChangeTypeEnum.SPENDING_REWARDS);// 增加用户经验、更新用户等级userLevelUtil.updateUserLevelUtil(order.getPrice(), userId, PayTypeEnum.BALANCE, false);//消费返佣inviteNewRecordService.grantInviteNewAward(order.getUserId(), InviteNewGiftTypeEnum.COMMISSION.getCode(), order.getPrice());} else {// 支付失败// 订单状态改为取消或者支付异常order.setPayStatus(VipCardOrderStatusEnum.CANCELLED.getCode());order.setCancelTime(new Date());userVipCardOrderMapper.updateById(order);// 回退会员卡库存vipCardMapper.addInventory(order.getCardId(), 1);}}/*** 计算会员卡的过期日期** @param type 会员卡类型 0周卡 1月卡* @param expiredDate 会员卡现在的过期日期,没有就取当前时间* @return 计算出来的过期日期*/private Date getExpiredDate(Integer type, Date expiredDate) {if (expiredDate == null) {log.error("会员卡现在的过期日期不能为空");
// throw new BaseException("会员卡现在的过期日期不能为空");return new Date();}if (type == 0) {// 周卡// 周卡有效期7天int days = 7;return DateUtils.addDateDays(expiredDate, days);} else if (type == 1) {// 月卡// 月卡有效期30天int days = 30;return DateUtils.addDateDays(expiredDate, days);} else {log.error("会员卡类型错误");
// throw new BaseException("会员卡类型错误");return new Date();}}
}
获取配置方法(这里的配置可以直接写在yml配置文件中):
/*** 根据Type获取系统参数** @param type 类型* @return 参数值*/public static List<SysSetting> getSdParam(String type) {SysSettingMapper sysSettingDao = ApplicationContextHelper.popBean(SysSettingMapper.class);List<SysSetting> sysBase = sysSettingDao.selectList(new LambdaQueryWrapper<SysSetting>().eq(SysSetting::getType, type));return sysBase;}