添加新的支付方式:Stripe
This commit is contained in:
@@ -11,6 +11,8 @@ public enum CreditsEventsEnum {
|
||||
// PRICE("price","0.1"),
|
||||
|
||||
BUY_CREDITS("Buy Credits","60"),
|
||||
|
||||
REFUND("Refund","60"),
|
||||
// BUY_CREDITS("Buy Credits","10"),
|
||||
|
||||
// 每月更新
|
||||
|
||||
@@ -24,7 +24,12 @@ public enum PayTypeEnum {
|
||||
/**
|
||||
* 香港支付宝
|
||||
*/
|
||||
ALIPAY_HK("Alipay-HK");
|
||||
ALIPAY_HK("Alipay-HK"),
|
||||
|
||||
/**
|
||||
* Stripe
|
||||
*/
|
||||
STRIPE("Stripe");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
|
||||
40
src/main/java/com/ai/da/common/task/StripeTask.java
Normal file
40
src/main/java/com/ai/da/common/task/StripeTask.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.ai.da.common.task;
|
||||
|
||||
import com.ai.da.common.enums.PayTypeEnum;
|
||||
import com.ai.da.mapper.primary.entity.OrderInfo;
|
||||
import com.ai.da.service.OrderInfoService;
|
||||
import com.ai.da.service.StripeService;
|
||||
import com.paypal.http.exceptions.SerializeException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class StripeTask {
|
||||
|
||||
@Resource
|
||||
private OrderInfoService orderInfoService;
|
||||
|
||||
@Resource
|
||||
private StripeService stripeService;
|
||||
|
||||
// @Scheduled(cron = "0/30 * * * * ?")
|
||||
public void orderConfirm() throws SerializeException {
|
||||
|
||||
// 查看超过30分钟以上仍未支付的订单 置为超时订单
|
||||
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.STRIPE.getType());
|
||||
|
||||
for (OrderInfo orderInfo : orderInfoList) {
|
||||
String orderNo = orderInfo.getOrderNo();
|
||||
log.warn("超时订单 ===> {}", orderNo);
|
||||
|
||||
//核实订单状态:调用支付宝查单接口
|
||||
stripeService.checkOrderStatus(orderNo);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.service.StripeService;
|
||||
import com.paypal.http.HttpResponse;
|
||||
import com.paypal.payments.Refund;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -10,7 +12,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@Api(tags = "Stripe模块")
|
||||
@@ -24,8 +25,8 @@ public class StripeController {
|
||||
|
||||
@ApiOperation("创建支付链接")
|
||||
@PostMapping("/createOrder/{amount}")
|
||||
public Response<String> pay(@PathVariable Integer amount) {
|
||||
return Response.success(stripeService.pay(amount));
|
||||
public Response<String> pay(@PathVariable Integer amount, @RequestParam String returnUrl) {
|
||||
return Response.success(stripeService.pay(amount, returnUrl));
|
||||
}
|
||||
|
||||
@ApiOperation("支付通知")
|
||||
@@ -38,4 +39,16 @@ public class StripeController {
|
||||
return Response.fail(400,"failure");
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation("申请退款")
|
||||
@PostMapping("/trade/refund/{orderNo}/{reason}")
|
||||
public Response<HttpResponse<Refund>> refund(@PathVariable String orderNo, @PathVariable String reason) throws IOException {
|
||||
String response = stripeService.refund(null,orderNo,reason);
|
||||
if (response.equals("退款成功")){
|
||||
return Response.success();
|
||||
}else {
|
||||
return Response.fail("Request for refund failed.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.ai.da.service;
|
||||
|
||||
import com.ai.da.mapper.primary.entity.PaymentInfo;
|
||||
import com.ai.da.model.dto.AlipayHKCallbackDTO;
|
||||
import com.paypal.orders.Order;
|
||||
import com.stripe.model.checkout.Session;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -14,4 +16,10 @@ public interface PaymentInfoService {
|
||||
void createPaymentInfoForPayPal(Order order);
|
||||
|
||||
void createPaymentInfoForAliPayHK(AlipayHKCallbackDTO alipayHKCallbackDTO);
|
||||
|
||||
void createPaymentInfoForStripe(Session session);
|
||||
|
||||
PaymentInfo getPaymentInfoByOrderId(String orderId);
|
||||
|
||||
void updatePaymentStatusById(Long id, String status, String content);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public interface StripeService {
|
||||
|
||||
String pay(Integer quantity);
|
||||
String pay(Integer quantity, String returnUrl);
|
||||
|
||||
Boolean notify(HttpServletRequest request);
|
||||
|
||||
String refund(String amount, String orderId, String reason);
|
||||
|
||||
void checkOrderStatus(String orderNo);
|
||||
}
|
||||
|
||||
@@ -210,12 +210,13 @@ public class AliPayServiceImpl implements AliPayService {
|
||||
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
|
||||
//记录支付日志
|
||||
paymentInfoService.createPaymentInfoForAliPay(params);
|
||||
float quantity = Float.parseFloat(totalAmount) / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
|
||||
// 更新积分
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(),Float.parseFloat(totalAmount) / Float.parseFloat(CreditsEventsEnum.PRICE.getValue()));
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(), quantity);
|
||||
// 添加积分变更记录
|
||||
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
|
||||
CreditsEventsEnum.BUY_CREDITS.getName() + "--Alipay",
|
||||
CreditsEventsEnum.BUY_CREDITS.getValue(),
|
||||
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
|
||||
"positive");
|
||||
} finally {
|
||||
//要主动释放锁
|
||||
@@ -312,12 +313,13 @@ public class AliPayServiceImpl implements AliPayService {
|
||||
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
|
||||
//并记录支付日志
|
||||
paymentInfoService.createPaymentInfoForAliPay(alipayTradeQueryResponse);
|
||||
float quantity = orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
|
||||
// 更新积分
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(),orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue()));
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(), quantity);
|
||||
// 添加积分变更记录
|
||||
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
|
||||
CreditsEventsEnum.BUY_CREDITS.getName() + "--Alipay",
|
||||
CreditsEventsEnum.BUY_CREDITS.getValue(),
|
||||
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
|
||||
"positive");
|
||||
}
|
||||
|
||||
|
||||
@@ -241,12 +241,13 @@ public class AlipayHKServiceImpl implements AlipayHKService {
|
||||
//记录支付日志
|
||||
paymentInfoService.createPaymentInfoForAliPayHK(alipayHKCallbackDTO);
|
||||
log.info("Alipay-HK 订单:{} 支付信息状态更新成功",orderNo);
|
||||
float quantity = Float.parseFloat(totalAmount) / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
|
||||
// 更新积分
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(),Float.parseFloat(totalAmount) / Float.parseFloat(CreditsEventsEnum.PRICE.getValue()));
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(), quantity);
|
||||
// 添加积分变更记录
|
||||
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
|
||||
CreditsEventsEnum.BUY_CREDITS.getName() + "--AlipayHK",
|
||||
CreditsEventsEnum.BUY_CREDITS.getValue(),
|
||||
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
|
||||
"positive");
|
||||
log.info("用户:{} 积分信息更新成功",orderByOrderNo.getAccountId());
|
||||
} finally {
|
||||
|
||||
@@ -104,7 +104,14 @@ public class CreditsServiceImpl extends ServiceImpl<CreditsDetailMapper, Credits
|
||||
BigDecimal existingCredits = accountMapper.selectById(accountId).getCredits();
|
||||
BigDecimal newCredits = new BigDecimal(CreditsEventsEnum.BUY_CREDITS.getValue()).multiply(new BigDecimal(quantity));
|
||||
BigDecimal subtracted = existingCredits.subtract(newCredits);
|
||||
// 更新t_account表
|
||||
accountService.updateCredits(accountId, subtracted.toString());
|
||||
// 更新t_credits_details表
|
||||
// 添加积分变更记录
|
||||
insertToCreditsDetail(accountId,
|
||||
CreditsEventsEnum.REFUND.getName() + "--Stripe",
|
||||
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
|
||||
"negative");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -586,12 +586,13 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
|
||||
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.SUCCESS);
|
||||
//记录支付日志
|
||||
paymentInfoService.createPaymentInfoForPayPal(capturedOrder);
|
||||
float quantity = orderInfo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
|
||||
// 更新积分
|
||||
creditsService.buyCredits(orderInfo.getAccountId(), orderInfo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue()));
|
||||
creditsService.buyCredits(orderInfo.getAccountId(), quantity);
|
||||
// 添加积分变更记录
|
||||
creditsService.insertToCreditsDetail(orderInfo.getAccountId(),
|
||||
CreditsEventsEnum.BUY_CREDITS.getName() + "--PayPal",
|
||||
CreditsEventsEnum.BUY_CREDITS.getValue(),
|
||||
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
|
||||
"positive");
|
||||
}
|
||||
}
|
||||
@@ -628,13 +629,14 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
|
||||
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
|
||||
//并记录支付日志
|
||||
paymentInfoService.createPaymentInfoForPayPal(result);
|
||||
float quantity = orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
|
||||
// 更新积分
|
||||
// creditsService.buyCredits(orderByOrderNo.getAccountId(),orderByOrderNo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(),orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue()));
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(), quantity);
|
||||
// 添加积分变更记录
|
||||
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
|
||||
CreditsEventsEnum.BUY_CREDITS.getName() + "--Paypal",
|
||||
CreditsEventsEnum.BUY_CREDITS.getValue(),
|
||||
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
|
||||
"positive");
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,16 @@ 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.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.google.gson.Gson;
|
||||
import com.paypal.orders.Order;
|
||||
import com.stripe.model.checkout.Session;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -146,4 +149,45 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
|
||||
|
||||
baseMapper.insert(paymentInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createPaymentInfoForStripe(Session session){
|
||||
String orderId = session.getMetadata().get("orderId");
|
||||
String status = session.getStatus();
|
||||
String sessionId = session.getId();
|
||||
Long amountTotal = session.getAmountTotal();
|
||||
// stripe 的支付金额单位是分
|
||||
Float divide = new BigDecimal(amountTotal).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).floatValue();
|
||||
|
||||
PaymentInfo paymentInfo = new PaymentInfo();
|
||||
paymentInfo.setOrderNo(orderId);
|
||||
paymentInfo.setPaymentType(PayTypeEnum.STRIPE.getType());
|
||||
paymentInfo.setTransactionId(sessionId);
|
||||
paymentInfo.setTradeType("电脑网站支付");
|
||||
paymentInfo.setTradeState(status);
|
||||
paymentInfo.setPayerTotal(divide);
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(session);
|
||||
paymentInfo.setContent(json);
|
||||
|
||||
baseMapper.insert(paymentInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentInfo getPaymentInfoByOrderId(String orderId){
|
||||
QueryWrapper<PaymentInfo> qw = new QueryWrapper<>();
|
||||
qw.eq("order_no", orderId);
|
||||
|
||||
return baseMapper.selectOne(qw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePaymentStatusById(Long id, String status, String content){
|
||||
PaymentInfo paymentInfo = new PaymentInfo();
|
||||
paymentInfo.setId(id);
|
||||
paymentInfo.setTradeState(status);
|
||||
paymentInfo.setContent(content);
|
||||
|
||||
baseMapper.updateById(paymentInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,36 @@
|
||||
package com.ai.da.service.impl;
|
||||
|
||||
import com.ai.da.service.PayPalCheckoutService;
|
||||
import com.ai.da.service.StripeService;
|
||||
import com.ai.da.common.enums.AliPayTradeStateEnum;
|
||||
import com.ai.da.common.enums.CreditsEventsEnum;
|
||||
import com.ai.da.common.enums.OrderStatusEnum;
|
||||
import com.ai.da.common.enums.PayTypeEnum;
|
||||
import com.ai.da.common.utils.OrderNoUtils;
|
||||
import com.ai.da.mapper.primary.entity.OrderInfo;
|
||||
import com.ai.da.mapper.primary.entity.PaymentInfo;
|
||||
import com.ai.da.mapper.primary.entity.RefundInfo;
|
||||
import com.ai.da.service.*;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.google.gson.Gson;
|
||||
import com.stripe.Stripe;
|
||||
import com.stripe.exception.SignatureVerificationException;
|
||||
import com.stripe.exception.StripeException;
|
||||
import com.stripe.model.*;
|
||||
import com.stripe.model.checkout.Session;
|
||||
import com.stripe.model.issuing.Card;
|
||||
import com.stripe.net.Webhook;
|
||||
import com.stripe.param.*;
|
||||
import com.stripe.param.checkout.SessionCreateParams;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@@ -30,7 +39,257 @@ public class StripeServiceImpl implements StripeService {
|
||||
private static final String privateKey = "sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2";
|
||||
// private static final String privateKey = "pk_test_51P4ZZL02n1TEydyNluht6VDxnBMoBw8S524otzlXV3CMCZI8HmkcUr3CKy8Z0eQ2WssSJ9sAaZnIQJI04fARHHJi00pcIB4sKU";
|
||||
|
||||
@Resource
|
||||
private OrderInfoService orderInfoService;
|
||||
@Resource
|
||||
private PayPalCheckoutService payPalCheckoutService;
|
||||
@Resource
|
||||
private PaymentInfoService paymentInfoService;
|
||||
@Resource
|
||||
private CreditsService creditsService;
|
||||
@Resource
|
||||
private RefundInfoService refundInfoService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String pay(Integer quantity, String returnUrl) {
|
||||
Stripe.apiKey = privateKey;
|
||||
log.info("生成订单");
|
||||
OrderInfo orderInfo = orderInfoService.createOrderByProductId(quantity, PayTypeEnum.STRIPE.getType());
|
||||
|
||||
try {
|
||||
//创建产品
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("name", "AiDA credits purchase");
|
||||
Product product = Product.create(params);
|
||||
String orderId = OrderNoUtils.getOrderNo();
|
||||
|
||||
BigDecimal actualAmount = new BigDecimal(Long.parseLong(CreditsEventsEnum.PRICE.getValue()) * 100); //stripe的默认单位是分,即传入的amount实际上小数点会被左移两位
|
||||
//给price绑定元数据并更新price用于检索
|
||||
Map<String, Object> metadata = new HashMap<>();
|
||||
metadata.put("orderId", orderId);
|
||||
//创建价格
|
||||
Map<String, Object> priceParams = new HashMap<>();
|
||||
priceParams.put("metadata", metadata); //通过订单号关联用于检索price信息(可选)
|
||||
priceParams.put("unit_amount", actualAmount.intValue());
|
||||
priceParams.put("currency", "HKD");
|
||||
priceParams.put("product", product.getId());
|
||||
Price price = Price.create(priceParams);
|
||||
|
||||
//创建支付信息得到url
|
||||
SessionCreateParams params3 = SessionCreateParams.builder()
|
||||
.setMode(SessionCreateParams.Mode.PAYMENT)
|
||||
.setSuccessUrl(returnUrl) //可自定义成功页面
|
||||
.addLineItem(
|
||||
SessionCreateParams.LineItem.builder()
|
||||
.setQuantity(Long.valueOf(quantity))
|
||||
.setPrice(price.getId())
|
||||
.build())
|
||||
.putMetadata("orderId", orderId) //通过订单号关联用于检索支付信息(可选)
|
||||
.build();
|
||||
Session session = Session.create(params3);
|
||||
log.info("sessionId:" + session.getId()); //退款方式1:拿到sessionId入库,退款的时候根据这个id找到PaymentIntent的id然后发起退款
|
||||
|
||||
// 更新order信息
|
||||
orderInfoService.updateOrderNoById(orderInfo.getId(), orderId);
|
||||
//记录支付日志
|
||||
paymentInfoService.createPaymentInfoForStripe(session);
|
||||
return session.getUrl();
|
||||
} catch (Exception e) {
|
||||
log.error("创建支付会话出现异常:", e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean notify(HttpServletRequest request) {
|
||||
log.info("stripe异步通知进行中");
|
||||
String payload = null;
|
||||
String sigHeader = null;
|
||||
String endpointSecret = "whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w";//webhook秘钥签名
|
||||
try {
|
||||
sigHeader = request.getHeader("Stripe-Signature");
|
||||
payload = payPalCheckoutService.getBody(request);
|
||||
} catch (Exception e) {
|
||||
log.info("stripe 支付回调参数解析异常:errorMsg {}", e.getMessage());
|
||||
log.info("request sigHeader = {}", sigHeader);
|
||||
log.info("request body = {}", payload);
|
||||
e.printStackTrace();
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
Event event = null;
|
||||
try {
|
||||
assert sigHeader != null;
|
||||
event = Webhook.constructEvent(payload, sigHeader, endpointSecret);
|
||||
} catch (SignatureVerificationException e) {
|
||||
log.info("stripe 验签,获取事件异常, errorMsg=" + e.getMessage());
|
||||
log.info("request sigHeader = {}", sigHeader);
|
||||
log.info("request body = {}", payload);
|
||||
e.printStackTrace();
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
//获取自定义参数
|
||||
// Deserialize the nested object inside the event
|
||||
assert event != null;
|
||||
EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer();
|
||||
StripeObject stripeObject = null;
|
||||
if (dataObjectDeserializer.getObject().isPresent()) {
|
||||
stripeObject = dataObjectDeserializer.getObject().get();
|
||||
} else {
|
||||
log.info("stripe 验签失败!");
|
||||
log.info("request sigHeader = {}", sigHeader);
|
||||
log.info("request body = {}", payload);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
log.info("stripe验签成功");
|
||||
|
||||
if (event.getType().equals("checkout.session.completed")) {
|
||||
Session session = (Session) stripeObject;
|
||||
processOrder(session);
|
||||
}
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
public void processOrder(Session session) {
|
||||
String orderId = session.getMetadata().get("orderId");
|
||||
float totalAmount = new BigDecimal(session.getAmountTotal()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).floatValue();
|
||||
|
||||
try {
|
||||
//处理重复通知
|
||||
//接口调用的幂等性:无论接口被调用多少次,以下业务执行一次
|
||||
// String orderStatus = orderInfoService.getOrderStatus(orderNo);
|
||||
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderId);
|
||||
String orderStatus = orderByOrderNo.getOrderStatus();
|
||||
// 当订单状态处于未支付或超时已关闭时,更新订单状态,其他状态均不更新订单状态
|
||||
if (!OrderStatusEnum.NOT_PAY.getType().equals(orderStatus) && !OrderStatusEnum.TIMEOUT_CLOSED.getType().equals(orderStatus)) {
|
||||
log.info("订单状态 : {}", orderStatus);
|
||||
return;
|
||||
}
|
||||
//更新订单状态
|
||||
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.SUCCESS);
|
||||
log.info("Stripe 订单:{} 状态更新成功", orderId);
|
||||
|
||||
// 更新支付日志
|
||||
paymentInfoService.updatePaymentStatusById(
|
||||
paymentInfoService.getPaymentInfoByOrderId(orderId).getId(),
|
||||
session.getStatus(),
|
||||
new Gson().toJson(session));
|
||||
|
||||
log.info("Stripe 订单:{} 支付信息状态更新成功", orderId);
|
||||
float quantity = totalAmount / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
|
||||
// 更新积分
|
||||
creditsService.buyCredits(orderByOrderNo.getAccountId(), quantity);
|
||||
// 添加积分变更记录
|
||||
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
|
||||
CreditsEventsEnum.BUY_CREDITS.getName() + "--Stripe",
|
||||
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
|
||||
"positive");
|
||||
log.info("用户:{} 积分信息更新成功", orderByOrderNo.getAccountId());
|
||||
}catch (Exception e){
|
||||
log.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String refund(String amount, String orderId, String reason) {
|
||||
Refund refund ;
|
||||
RefundInfo refundByOrderNo = refundInfoService.createRefundByOrderNo(orderId, reason);
|
||||
|
||||
try {
|
||||
Stripe.apiKey = privateKey;
|
||||
// 根据orderId找到对应的sessionId
|
||||
String sessionId = paymentInfoService.getPaymentInfoByOrderId(orderId).getTransactionId();
|
||||
|
||||
if (StringUtils.isNotEmpty(sessionId)) { //根据会话编号退款
|
||||
Session session = Session.retrieve(sessionId);
|
||||
RefundCreateParams params;
|
||||
if (amount != null && !amount.equals("0")) { //指定退款金额
|
||||
BigDecimal actualAmount = new BigDecimal(amount).multiply(BigDecimal.valueOf(100)); //api默认单位分
|
||||
params = RefundCreateParams.builder()
|
||||
.setPaymentIntent(session.getPaymentIntent())
|
||||
.setAmount(actualAmount.longValue())
|
||||
.build();
|
||||
} else { //全额退款
|
||||
params = RefundCreateParams.builder()
|
||||
.setPaymentIntent(session.getPaymentIntent())
|
||||
.build();
|
||||
}
|
||||
refund = Refund.create(params);
|
||||
log.info("根据会话编号退款成功");
|
||||
|
||||
}else {
|
||||
log.error("当前订单不存在");
|
||||
return "退款异常";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//e.getMessage.contain("charge_already_refunded") 已退款
|
||||
//e.getMessage.contain("resource_missing") 退款编号错误
|
||||
//e.getMessage.contain("amount on charge ($n)") 金额应小于n
|
||||
log.error("退款异常:", e);
|
||||
return "退款异常";
|
||||
}
|
||||
|
||||
if ("succeeded".equals(refund.getStatus())) {
|
||||
//进行数据库操作,修改状态为已退款(配合回调和退款查询确定退款成功)
|
||||
//更新订单状态
|
||||
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_SUCCESS);
|
||||
|
||||
refundInfoService.updateRefundForPayPal(
|
||||
refundByOrderNo.getId(),
|
||||
refund.getId(),
|
||||
new Gson().toJson(refund),
|
||||
AliPayTradeStateEnum.REFUND_SUCCESS.getType()); //退款成功
|
||||
|
||||
// 更新积分状态
|
||||
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderId);
|
||||
creditsService.creditsRefund(orderByOrderNo.getAccountId(), (int)(orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue())));
|
||||
} else {
|
||||
//更新订单状态
|
||||
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_ABNORMAL);
|
||||
|
||||
//更新退款单
|
||||
refundInfoService.updateRefundForPayPal(
|
||||
refundByOrderNo.getId(),
|
||||
refund.getId(),
|
||||
new Gson().toJson(refund),
|
||||
AliPayTradeStateEnum.REFUND_ERROR.getType()); //退款失败
|
||||
}
|
||||
log.info("记录退款订单");
|
||||
return "退款成功";
|
||||
}
|
||||
|
||||
public void checkOrderStatus(String orderNo){
|
||||
Stripe.apiKey = privateKey;
|
||||
// 1、通过orderNo 查询sessionId
|
||||
PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOrderId(orderNo);
|
||||
try {
|
||||
Session session = Session.retrieve(paymentInfo.getTransactionId());
|
||||
if (Objects.isNull(session)){
|
||||
log.warn("核实订单未创建 ===> {}", orderNo);
|
||||
return;
|
||||
} else if (session.getStatus().equals("open") || session.getStatus().equals("expired")){
|
||||
// 订单未支付 || 订单过期 ---> 均设置为超时未支付
|
||||
log.info("订单超时未支付 ===> {}", orderNo);
|
||||
//更新本地订单状态
|
||||
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.TIMEOUT_CLOSED);
|
||||
paymentInfoService.updatePaymentStatusById(paymentInfo.getId(),
|
||||
session.getStatus(),
|
||||
new Gson().toJson(session));
|
||||
} else if (session.getStatus().equals("complete")) {
|
||||
// 订单已完成
|
||||
processOrder(session);
|
||||
}
|
||||
} catch (StripeException e) {
|
||||
log.error("根据sessionId获取Stripe Session失败");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 1、创建customer,获取customerId
|
||||
// 2、创建客户支付方式 (从前端获取)
|
||||
@@ -72,25 +331,6 @@ public class StripeServiceImpl implements StripeService {
|
||||
.build();
|
||||
|
||||
PaymentMethod paymentMethod = PaymentMethod.create(params);
|
||||
|
||||
/*try{
|
||||
Map<String, Object> card = new HashMap<>();
|
||||
card.put("exp_month",8L);
|
||||
card.put("exp_year",2026L);
|
||||
card.put("number","4242 4242 4242 42422");
|
||||
card.put("cvc","314");
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("type", "card");
|
||||
params.put("card", card);
|
||||
PaymentMethod paymentMethod = PaymentMethod.create(params);
|
||||
Map<String, Object> params2 = new HashMap<>();
|
||||
params2.put("customer", customerId);
|
||||
paymentMethod.attach(params2);
|
||||
return paymentMethod.getId();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}*/
|
||||
// return null;
|
||||
return paymentMethod.getId();
|
||||
}
|
||||
|
||||
@@ -142,144 +382,5 @@ public class StripeServiceImpl implements StripeService {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws StripeException {
|
||||
// String customerId = createCustomer();
|
||||
// log.info(customerId);
|
||||
String paymentMethodId = createPaymentMethod("cus_Pvc92hQPBgLE2t");
|
||||
log.info(paymentMethodId);
|
||||
// String substring = paymentMethodId.substring(0, paymentMethodId.lastIndexOf("_"));
|
||||
// log.info(substring);
|
||||
// log.info(substring.substring(0, substring.lastIndexOf("_")));
|
||||
// String paymentIntentId = createPaymentIntent(null, "cus_Pvc92hQPBgLE2t");
|
||||
// log.info(paymentIntentId);
|
||||
|
||||
// confirmPaymentIntent("pi_3P5lr002n1TEydyN0caeqdfq");
|
||||
|
||||
}
|
||||
|
||||
public String pay(Integer quantity) {
|
||||
Stripe.apiKey = privateKey;
|
||||
try {
|
||||
//创建产品
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("name", "AiDA 积分");
|
||||
// PriceCreateParams.ProductData productData = new PriceCreateParams.ProductData.Builder().setName("AiDA 积分").build();
|
||||
Product product = Product.create(params);
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
String format = simpleDateFormat.format(new Date());
|
||||
|
||||
//创建价格
|
||||
Map<String, Object> priceParams = new HashMap<>();
|
||||
// BigDecimal actualAmount = createOrderEntity.getAmount().multiply(BigDecimal.valueOf(100)); //stripe的默认单位是分,即传入的amount实际上小数点会被左移两位
|
||||
BigDecimal actualAmount = new BigDecimal("600"); //stripe的默认单位是分,即传入的amount实际上小数点会被左移两位
|
||||
//给price绑定元数据并更新price用于检索
|
||||
Map<String, Object> metadata = new HashMap<>();
|
||||
metadata.put("orderId", format);
|
||||
priceParams.put("metadata", metadata); //通过订单号关联用于检索price信息(可选)
|
||||
priceParams.put("unit_amount", actualAmount.intValue());
|
||||
priceParams.put("currency", "HKD");
|
||||
priceParams.put("product", product.getId());
|
||||
// priceParams.put("product_data", product.getId());
|
||||
// PriceCreateParams priceParam = PriceCreateParams.builder().setCurrency("HKD").setProductData(productData).setUnitAmount(Long.valueOf("6")).build();
|
||||
Price price = Price.create(priceParams);
|
||||
|
||||
//创建支付信息得到url
|
||||
SessionCreateParams params3 = SessionCreateParams.builder()
|
||||
.setMode(SessionCreateParams.Mode.PAYMENT)
|
||||
.setSuccessUrl("https://www.example.com") //可自定义成功页面
|
||||
.setCancelUrl("https://www.example.com")
|
||||
.addLineItem(
|
||||
SessionCreateParams.LineItem.builder()
|
||||
.setQuantity(Long.valueOf(quantity))
|
||||
/*.setPriceData(
|
||||
new SessionCreateParams.LineItem.PriceData.Builder()
|
||||
.setCurrency("HKD")
|
||||
.setUnitAmount(Long.valueOf("600"))
|
||||
.setProductData(
|
||||
new SessionCreateParams.LineItem.PriceData.ProductData.Builder()
|
||||
.setName("AiDaA 积分")
|
||||
.build())
|
||||
.build())*/
|
||||
.setPrice(price.getId())
|
||||
.build())
|
||||
.putMetadata("orderId",format) //通过订单号关联用于检索支付信息(可选)
|
||||
.build();
|
||||
Session session = Session.create(params3);
|
||||
System.out.println("sessionId:" +session.getId());
|
||||
String sessionId = session.getId(); //退款方式1:拿到sessionId入库,退款的时候根据这个id找到PaymentIntent的id然后发起退款
|
||||
return session.getUrl();
|
||||
}catch (Exception e){
|
||||
log.error("创建支付会话出现异常:",e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Resource
|
||||
private PayPalCheckoutService payPalCheckoutService;
|
||||
|
||||
@Override
|
||||
public Boolean notify(HttpServletRequest request){
|
||||
log.info("stripe异步通知进行中");
|
||||
String payload = null;
|
||||
String sigHeader= null;
|
||||
// String endpointSecret = "whsec_xxxgx92OjXdBf1lc2c";//webhook秘钥签名
|
||||
String endpointSecret = "whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w";//webhook秘钥签名
|
||||
try {
|
||||
sigHeader = request.getHeader("Stripe-Signature");
|
||||
payload = payPalCheckoutService.getBody(request);
|
||||
} catch (Exception e) {
|
||||
log.info("stripe 支付回调参数解析异常:errorMsg {}", e.getMessage());
|
||||
log.info("request sigHeader = {}", sigHeader);
|
||||
log.info("request body = {}", payload);
|
||||
e.printStackTrace();
|
||||
return Boolean.FALSE;
|
||||
// response.setStatus(400);
|
||||
}
|
||||
|
||||
Event event = null;
|
||||
try {
|
||||
assert sigHeader != null;
|
||||
event = Webhook.constructEvent(payload, sigHeader, endpointSecret);
|
||||
} catch (SignatureVerificationException e) {
|
||||
log.info("stripe 验签,获取事件异常, errorMsg=" + e.getMessage());
|
||||
log.info("request sigHeader = {}", sigHeader);
|
||||
log.info("request body = {}", payload);
|
||||
e.printStackTrace();
|
||||
return Boolean.FALSE;
|
||||
// response.setStatus(400);
|
||||
}
|
||||
|
||||
|
||||
//获取自定义参数
|
||||
// Deserialize the nested object inside the event
|
||||
assert event != null;
|
||||
EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer();
|
||||
StripeObject stripeObject = null;
|
||||
if (dataObjectDeserializer.getObject().isPresent()) {
|
||||
stripeObject = dataObjectDeserializer.getObject().get();
|
||||
|
||||
} else {
|
||||
log.info("stripe 验签失败!");
|
||||
log.info("request sigHeader = {}", sigHeader);
|
||||
log.info("request body = {}", payload);
|
||||
return Boolean.FALSE;
|
||||
// response.setStatus(400);
|
||||
}
|
||||
|
||||
if (event.getType().equals("checkout.session.completed")){
|
||||
|
||||
}
|
||||
log.info("stripe验签成功");
|
||||
return Boolean.TRUE;
|
||||
// response.setStatus(200);
|
||||
|
||||
|
||||
// 1、验签
|
||||
|
||||
// 2、处理订单
|
||||
// 2.1 更新订单状态
|
||||
// 2.2 添加积分
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user