Alipay-HK 创建订单和异步回调

This commit is contained in:
2024-05-15 15:38:11 +08:00
parent d1fab003d2
commit 3790b8ee72
16 changed files with 429 additions and 69 deletions

View File

@@ -1,4 +1,14 @@
package com.ai.da.service;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
import java.util.Map;
public interface AlipayHKService {
String createOrder(Integer amount, String wallet);
String callback(Map<String, String> params);
void processOrder(AlipayHKCallbackDTO alipayHKCallbackDTO);
}

View File

@@ -1,5 +1,6 @@
package com.ai.da.service;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
import com.paypal.orders.Order;
import java.util.Map;
@@ -11,4 +12,6 @@ public interface PaymentInfoService {
void createPaymentInfoForAliPay(Map<String, String> params);
void createPaymentInfoForPayPal(Order order);
void createPaymentInfoForAliPayHK(AlipayHKCallbackDTO alipayHKCallbackDTO);
}

View File

@@ -1,50 +1,113 @@
package com.ai.da.service.impl;
import com.ai.da.common.constant.AlipayHKConstant;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.service.AlipayHKService;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.enums.PayTypeEnum;
import com.ai.da.common.utils.AlipayHKEncryptionUtil;
import com.ai.da.common.utils.AlipayHKRequestUtil;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
import com.ai.da.model.dto.AlipayHKRequestDTO;
import com.ai.da.service.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
@Service
@Slf4j
public class AlipayHKServiceImpl implements AlipayHKService {
@Value("${alipay.hk.merchant-id}")
private static String merchantId;
@Value("${alipayHK.merchantId}")
private String merchantId;
@Value("${alipay.hk.segment-id}")
private static String segmentId;
@Value("${alipayHK.segmentId}")
private String segmentId;
@Value("${alipay.hk.AESKey}")
private static String aesKey;
@Value("${alipayHK.AESKey}")
private String aesKey;
@Value("${alipay.hk.rsaPrivateKey}")
private static String privateKeyPath;
@Value("${alipayHK.rsaPrivateKey}")
private String privateKeyPath;
@Value("${alipay.hk.rsaPublicKey}")
private static String publicKeyPath;
@Value("${alipayHK.rsaPublicKey}")
private String publicKeyPath;
@Resource
private OrderInfoService orderInfoService;
@Resource
private PaymentInfoService paymentInfoService;
@Resource
private CreditsService creditsService;
@Resource
private AlipayHKEncryptionUtil alipayHKEncryptionUtil;
@Resource
private AlipayHKRequestUtil alipayHKRequestUtil;
/**
* 创建订单
*/
public void createOrder(Integer amount){
@Override
public String createOrder(Integer amount, String wallet){
HashMap<String, Object> param = new HashMap<>();
String orderRef = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
param.put("order_ref", orderRef);
param.put("amount", Integer.parseInt(CreditsEventsEnum.PRICE.getValue()) * amount);
param.put("subject", "AiDA Credits Purchase");
param.put("wallet", "ALIPAYHK");
param.put("segment_id", segmentId);
param.put("payment_solution", "WAP");
try{
HashMap<String, Object> param = new HashMap<>();
String orderRef = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
param.put("order_ref", orderRef);
param.put("amount", Integer.parseInt(CreditsEventsEnum.PRICE.getValue()) * amount);
param.put("subject", "AiDA Credits Purchase");
// ALIPAYHK 或者 ALIPAYCN
param.put("wallet", wallet);
param.put("segment_id", segmentId);
param.put("payment_solution", "WAP");
log.info("alipay-hk 创建订单,参数信息: {}", param);
// 生成订单
log.info("创建订单");
OrderInfo orderInfo = orderInfoService.createOrderByProductId(amount, PayTypeEnum.ALIPAY_HK.getType());
// 加密
AlipayHKRequestDTO alipayHKRequestDTO = alipayHKEncryptionUtil.AESCBCWithRSA(param, AlipayHKConstant.CREATE_ORDER);
// 请求Alipay服务端
String response = alipayHKRequestUtil.createOrder(alipayHKRequestDTO);
// 获取response中的加密数据
JSONObject responseObj = JSONObject.parseObject(response);
JSONObject resultObj = JSONObject.parseObject(responseObj.get("result").toString());
String nonce = resultObj.get("nonce").toString();
String message = resultObj.get("message").toString();
// 解密
String s = alipayHKEncryptionUtil.decryptAES(message, nonce);
JSONObject jsonObject = JSONObject.parseObject(s);
String orderId = jsonObject.get("out_trade_no").toString();
orderInfoService.updateOrderNoById(orderInfo.getId(), orderId);
log.info("create_order 接口返回信息 {}", s);
return s;
}catch (Exception e){
log.error("订单创建失败 {}", e.getMessage());
// todo 或者抛异常
return null;
}
}
@@ -53,8 +116,122 @@ public class AlipayHKServiceImpl implements AlipayHKService {
* 异步回调
* @return
*/
public void callback(){
public String callback(Map<String, String> params){
log.info("支付通知正在执行");
log.info("通知参数 ===> {}", params);
String result = "failure";
String data = params.get("data");
String signature = params.get("signature");
try {
// 异步回调验签
Boolean verification = alipayHKEncryptionUtil.signatureVerification(data, signature);
if (!verification){
log.error("alipay-hk 验签失败");
return result;
}
AlipayHKCallbackDTO alipayHKCallbackDTO = JSONObject.parseObject(data, AlipayHKCallbackDTO.class);
//按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,
//1 商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号
String outTradeNo = alipayHKCallbackDTO.getOut_trade_no();
OrderInfo order = orderInfoService.getOrderByOrderNo(outTradeNo);
if(order == null){
log.error("订单不存在");
return result;
}
//2 判断 total_amount 是否确实为该订单的实际金额(即商户订单创建时的金额)
Long totalAmount = alipayHKCallbackDTO.getAmount();
Long totalFee = order.getTotalFee().longValue();
if(totalAmount != totalFee){
log.error("金额校验失败");
return result;
}
//3 校验通知中的 seller_id或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方
String sellerId = alipayHKCallbackDTO.getMerchant_id();
String sellerIdProperty = merchantId;
if(!sellerId.equals(sellerIdProperty)){
log.error("商家pid校验失败");
return result;
}
//4 验证 app_id 是否为该商户本身
String segId = alipayHKCallbackDTO.getSegment_id();
String segmentIdProperty = segmentId;
if(!segId.equals(segmentIdProperty)){
log.error("segmentId校验失败");
return result;
}
//在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS时
// 支付宝才会认定为买家付款成功。
String tradeStatus = alipayHKCallbackDTO.getStatus();
if(!AlipayHKConstant.STATUS_PAID.equals(tradeStatus)){
log.error("支付未成功");
return result;
}
processOrder(alipayHKCallbackDTO);
result = "success";
}catch (Exception e){
log.error(e.getMessage());
}
return result;
}
private final ReentrantLock lock = new ReentrantLock();
@Transactional(rollbackFor = Exception.class)
@Override
public void processOrder(AlipayHKCallbackDTO alipayHKCallbackDTO) {
log.info("处理订单");
//获取订单号
String orderNo = alipayHKCallbackDTO.getOut_trade_no();
String totalAmount = alipayHKCallbackDTO.getAmount().toString();
/*在对业务数据进行状态检查和处理之前,
要采用数据锁进行并发控制,
以避免函数重入造成的数据混乱*/
//尝试获取锁:
// 成功获取则立即返回true获取失败则立即返回false。不必一直等待锁的释放
if(lock.tryLock()) {
try {
//处理重复通知
//接口调用的幂等性:无论接口被调用多少次,以下业务执行一次
// String orderStatus = orderInfoService.getOrderStatus(orderNo);
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderNo);
String orderStatus = orderByOrderNo.getOrderStatus();
// 当订单状态处于未支付或超时已关闭时,更新订单状态
if (!OrderStatusEnum.NOT_PAY.getType().equals(orderStatus) || !OrderStatusEnum.TIMEOUT_CLOSED.getType().equals(orderStatus)) {
return;
}
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfoForAliPayHK(alipayHKCallbackDTO);
// 添加积分变更记录
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--AlipayHK",
CreditsEventsEnum.BUY_CREDITS.getValue(),
"positive");
// 更新积分
creditsService.buyCredits(orderByOrderNo.getAccountId(),Integer.parseInt(totalAmount) / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
} finally {
//要主动释放锁
lock.unlock();
}
}
}
@@ -88,10 +265,28 @@ public class AlipayHKServiceImpl implements AlipayHKService {
public static void main(String[] args) throws Exception {
// test();
// AESCBCWithRSA();
// decrypt();
// public static void main(String[] args) throws Exception {
//// test();
//// AESCBCWithRSA();
//// decrypt();
// }
public static void main(String[] args) {
System.out.println("Supported TLS versions:");
String[] tlsVersions = getSupportedTLSVersions();
Arrays.stream(tlsVersions).forEach(System.out::println);
}
public static String[] getSupportedTLSVersions() {
try {
SSLContext context = SSLContext.getDefault();
SSLSocketFactory factory = context.getSocketFactory();
String[] supportedProtocols = factory.getDefaultCipherSuites();
return supportedProtocols;
} catch (Exception e) {
e.printStackTrace();
return new String[0];
}
}

View File

@@ -3,6 +3,7 @@ package com.ai.da.service.impl;
import com.ai.da.common.enums.PayTypeEnum;
import com.ai.da.mapper.primary.PaymentInfoMapper;
import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
import com.ai.da.service.PaymentInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
@@ -48,7 +49,8 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
paymentInfo.setTransactionId(transactionId);
paymentInfo.setTradeType(tradeType);
paymentInfo.setTradeState(tradeState);
paymentInfo.setPayerTotal(payerTotal);
// 原来的单位是:分 Int 现改为:元 Long
paymentInfo.setPayerTotal(payerTotal / 100L);
paymentInfo.setContent(plainText);
baseMapper.insert(paymentInfo);
@@ -80,7 +82,8 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
paymentInfo.setTransactionId(transactionId);
paymentInfo.setTradeType("电脑网站支付");
paymentInfo.setTradeState(tradeStatus);
paymentInfo.setPayerTotal(totalAmountInt);
// 原来的单位是分 Int 现改为元 Long
paymentInfo.setPayerTotal(totalAmountInt / 100L);
Gson gson = new Gson();
String json = gson.toJson(params, HashMap.class);
@@ -95,7 +98,7 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
log.info("记录支付日志");
// int totalAmountInt = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
int totalAmountInt = new BigDecimal(order.purchaseUnits().get(0).payments().captures().get(0).amount().value()).intValue();
Long totalAmountInt = new BigDecimal(order.purchaseUnits().get(0).payments().captures().get(0).amount().value()).longValue();
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setOrderNo(order.id());
@@ -103,6 +106,7 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
paymentInfo.setTransactionId(order.id());
paymentInfo.setTradeType("电脑网站支付");
paymentInfo.setTradeState(order.status());
// todo 确认这里的数据单位是不是元
paymentInfo.setPayerTotal(totalAmountInt);
Gson gson = new Gson();
@@ -111,4 +115,35 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
baseMapper.insert(paymentInfo);
}
@Override
public void createPaymentInfoForAliPayHK(AlipayHKCallbackDTO alipayHKCallbackDTO) {
log.info("记录支付日志");
//获取订单号
String orderNo = alipayHKCallbackDTO.getOut_trade_no();
//业务编号
String transactionId = alipayHKCallbackDTO.getTransaction_id();
//交易状态
String tradeStatus = alipayHKCallbackDTO.getStatus();
//交易金额
String totalAmount = alipayHKCallbackDTO.getAmount().toString();
// int totalAmountInt = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
Long totalAmountInt = new BigDecimal(totalAmount).longValue();
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setOrderNo(orderNo);
paymentInfo.setPaymentType(PayTypeEnum.ALIPAY_HK.getType());
paymentInfo.setTransactionId(transactionId);
paymentInfo.setTradeType("电脑网站支付");
paymentInfo.setTradeState(tradeStatus);
paymentInfo.setPayerTotal(totalAmountInt);
Gson gson = new Gson();
String json = gson.toJson(alipayHKCallbackDTO);
paymentInfo.setContent(json);
baseMapper.insert(paymentInfo);
}
}