1、接入paypal

2、修改支付宝支付
This commit is contained in:
2024-03-01 17:31:26 +08:00
parent e28502a00c
commit ee96759832
32 changed files with 1379 additions and 163 deletions

View File

@@ -1,5 +1,6 @@
package com.ai.da.service.impl;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.enums.AliPayTradeStateEnum;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.enums.PayTypeEnum;
@@ -12,6 +13,8 @@ import com.ai.da.service.RefundInfoService;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.google.gson.Gson;
@@ -69,7 +72,7 @@ public class AliPayServiceImpl implements AliPayService {
//组装当前业务方法的请求参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderInfo.getOrderNo());
BigDecimal total = new BigDecimal(orderInfo.getTotalFee().toString()).divide(new BigDecimal("100"));
BigDecimal total = new BigDecimal(orderInfo.getTotalFee().toString());
bizContent.put("total_amount", total);
bizContent.put("subject", orderInfo.getTitle());
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
@@ -84,14 +87,93 @@ public class AliPayServiceImpl implements AliPayService {
return response.getBody();
} else {
log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
throw new RuntimeException("创建支付交易失败");
throw new BusinessException("Order creation failed");
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("创建支付交易失败");
throw new BusinessException("Order creation failed");
}
}
@Override
public String tradeNotify(Map<String, String> params) {
log.info("支付通知正在执行");
log.info("通知参数 ===> {}", params);
String result = "failure";
try {
//异步通知验签
boolean signVerified = AlipaySignature.rsaCheckV1(
params,
config.getProperty("alipay.alipay-public-key"),
AlipayConstants.CHARSET_UTF8,
AlipayConstants.SIGN_TYPE_RSA2); //调用SDK验证签名
if(!signVerified){
//验签失败则记录异常日志并在response中返回failure.
log.error("支付成功异步通知验签失败!");
return result;
}
// 验签成功后
log.info("支付成功异步通知验签成功!");
//按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,
//1 商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号
String outTradeNo = params.get("out_trade_no");
OrderInfo order = orderInfoService.getOrderByOrderNo(outTradeNo);
if(order == null){
log.error("订单不存在");
return result;
}
//2 判断 total_amount 是否确实为该订单的实际金额(即商户订单创建时的金额)
String totalAmount = params.get("total_amount");
int totalAmountInt = new BigDecimal(totalAmount).intValue();
int totalFeeInt = order.getTotalFee().intValue();
if(totalAmountInt != totalFeeInt){
log.error("金额校验失败");
return result;
}
//3 校验通知中的 seller_id或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方
String sellerId = params.get("seller_id");
String sellerIdProperty = config.getProperty("alipay.seller-id");
if(!sellerId.equals(sellerIdProperty)){
log.error("商家pid校验失败");
return result;
}
//4 验证 app_id 是否为该商户本身
String appId = params.get("app_id");
String appIdProperty = config.getProperty("alipay.app-id");
if(!appId.equals(appIdProperty)){
log.error("appid校验失败");
return result;
}
//在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS时
// 支付宝才会认定为买家付款成功。
String tradeStatus = params.get("trade_status");
if(!"TRADE_SUCCESS".equals(tradeStatus)){
log.error("支付未成功");
return result;
}
//处理业务 修改订单状态 记录支付日志
processOrder(params);
//校验成功后在response中返回success并继续商户自身业务处理校验失败返回failure
result = "success";
} catch (AlipayApiException e) {
e.printStackTrace();
}
return result;
}
/**
* 处理订单
* @param params
@@ -112,26 +194,22 @@ public class AliPayServiceImpl implements AliPayService {
// 成功获取则立即返回true获取失败则立即返回false。不必一直等待锁的释放
if(lock.tryLock()) {
try {
//处理重复通知
//接口调用的幂等性:无论接口被调用多少次,以下业务执行一次
String orderStatus = orderInfoService.getOrderStatus(orderNo);
if (!OrderStatusEnum.NOT_PAY.getType().equals(orderStatus)) {
// 当订单状态处于未支付或超时已关闭时,更新订单状态
if (!OrderStatusEnum.NOT_PAY.getType().equals(orderStatus) || !OrderStatusEnum.TIMEOUT_CLOSED.getType().equals(orderStatus)) {
return;
}
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfoForAliPay(params);
} finally {
//要主动释放锁
lock.unlock();
}
}
}
/**
@@ -176,7 +254,7 @@ public class AliPayServiceImpl implements AliPayService {
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("查单接口的调用失败");
throw new BusinessException("查单接口的调用失败");
}
}
@@ -198,7 +276,7 @@ public class AliPayServiceImpl implements AliPayService {
if(result == null){
log.warn("核实订单未创建 ===> {}", orderNo);
//更新本地订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.CLOSED);
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.TIMEOUT_CLOSED);
}
//解析查单响应结果
@@ -209,20 +287,16 @@ public class AliPayServiceImpl implements AliPayService {
String tradeStatus = (String)alipayTradeQueryResponse.get("trade_status");
if(AliPayTradeStateEnum.NOTPAY.getType().equals(tradeStatus)){
log.warn("核实订单未支付 ===> {}", orderNo);
//如果订单未支付,则调用关单接口关闭订单
this.closeOrder(orderNo);
// 并更新商户端订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.CLOSED);
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.TIMEOUT_CLOSED);
}
if(AliPayTradeStateEnum.SUCCESS.getType().equals(tradeStatus)){
log.warn("核实订单已支付 ===> {}", orderNo);
//如果订单已支付,则更新商户端订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
//并记录支付日志
paymentInfoService.createPaymentInfoForAliPay(alipayTradeQueryResponse);
}
@@ -252,8 +326,8 @@ public class AliPayServiceImpl implements AliPayService {
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("关单接口的调用失败");
log.error("关单失败,原因 ===> {}",e.getMessage());
throw new BusinessException("关单接口的调用失败");
}
}
@@ -268,17 +342,14 @@ public class AliPayServiceImpl implements AliPayService {
try {
log.info("调用退款API");
//创建退款单
RefundInfo refundInfo = refundsInfoService.createRefundByOrderNoForAliPay(orderNo, reason);
//调用统一收单交易退款接口
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest ();
//组装当前业务方法的请求参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderNo);//订单编号
BigDecimal refund = new BigDecimal(refundInfo.getRefund().toString()).divide(new BigDecimal("100"));
BigDecimal refund = new BigDecimal(refundInfo.getRefund().toString());
//BigDecimal refund = new BigDecimal("2").divide(new BigDecimal("100"));
bizContent.put("refund_amount", refund);//退款金额:不能大于支付金额
bizContent.put("refund_reason", reason);//退款原因(可选)
@@ -290,10 +361,8 @@ public class AliPayServiceImpl implements AliPayService {
if(response.isSuccess()){
log.info("调用成功,返回结果 ===> " + response.getBody());
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.REFUND_SUCCESS);
//更新退款单
refundsInfoService.updateRefundForAliPay(
refundInfo.getRefundNo(),
@@ -302,21 +371,17 @@ public class AliPayServiceImpl implements AliPayService {
} else {
log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.REFUND_ABNORMAL);
//更新退款单
refundsInfoService.updateRefundForAliPay(
refundInfo.getRefundNo(),
response.getBody(),
AliPayTradeStateEnum.REFUND_ERROR.getType()); //退款失败
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("创建退款申请失败");
throw new BusinessException("创建退款申请失败");
}
}
@@ -349,7 +414,7 @@ public class AliPayServiceImpl implements AliPayService {
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("查单接口的调用失败");
throw new BusinessException("查单接口的调用失败");
}
}
@@ -363,7 +428,6 @@ public class AliPayServiceImpl implements AliPayService {
public String queryBill(String billDate, String type) {
try {
AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("bill_type", type);
@@ -383,13 +447,11 @@ public class AliPayServiceImpl implements AliPayService {
return billDownloadUrl;
} else {
log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
throw new RuntimeException("申请账单失败");
throw new BusinessException("申请账单失败");
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("申请账单失败");
log.error("申请账单失败,原因 ===> {}",e.getMessage());
throw new BusinessException("申请账单失败");
}
}
}

View File

@@ -0,0 +1,159 @@
package com.ai.da.service.impl;
import com.ai.da.common.config.PayPalClient;
import com.ai.da.common.constant.PayPalCheckoutConstant;
import com.ai.da.common.utils.paypalRequest.WebhookVerifyRequest;
import com.ai.da.service.CallBackService;
import com.ai.da.service.PayPalCheckoutService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.paypal.api.payments.Event;
import com.paypal.base.Constants;
import com.paypal.base.SDKUtil;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.ai.da.common.constant.PayPalCheckoutConstant.MODE;
// #Validate Webhook Sample
//
// This sample code demonstrates how to validate a webhook received on your
// web server. This sample assumes that you use the java servlet, which returns
// the HttpServletRequest object. However, you can modify this code to
// your specific case.
//
@Slf4j
@Service
public class CallBackServiceImpl implements CallBackService {
@Value("${paypal.client-id}")
private String clientId;
@Value("${paypal.client-secret}")
private String clientSecret;
@Resource
private PayPalClient payPalClient;
@Resource
private PayPalCheckoutService payPalCheckoutService;
@Override
public Boolean doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
return doPost(req, resp);
}
// ##Validate Webhook
protected Boolean doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
String body = getBody(req);
Map webhookEvent = new ObjectMapper().readValue(body, Map.class);
HashMap<String, Object> webhookRequest = new HashMap<>();
webhookRequest.put("auth_algo",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-AUTH-ALGO"));
webhookRequest.put("cert_url",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-CERT-URL"));
webhookRequest.put("transmission_id",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-ID"));
webhookRequest.put("transmission_sig",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-SIG"));
webhookRequest.put("transmission_time",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-TIME"));
webhookRequest.put("webhook_id",PayPalCheckoutConstant.WEBHOOK_ID);
webhookRequest.put("webhook_event",webhookEvent);
WebhookVerifyRequest webhookVerifyRequest = new WebhookVerifyRequest();
webhookVerifyRequest.authorization(payPalCheckoutService.getOAuth());
webhookVerifyRequest.requestBody(webhookRequest);
// 验签
HttpResponse<HashMap> verified = payPalClient.client(MODE, clientId, clientSecret).execute(webhookVerifyRequest);
boolean verifyResult = verified.result().get("verification_status").toString().equals("SUCCESS");
if (verifyResult){
// ### Api Context
APIContext apiContext = new APIContext(clientId, clientSecret, PayPalCheckoutConstant.MODE);
// Set the webhookId that you received when you created this webhook.
apiContext.addConfiguration(Constants.PAYPAL_WEBHOOK_ID, PayPalCheckoutConstant.WEBHOOK_ID);
Boolean result = Event.validateReceivedEvent(apiContext, getHeadersInfo(
req), body);
log.info("Webhook Validated: " + result);
if (result){
// 处理订单数据
LinkedHashMap<String,LinkedHashMap<String,String>> webhookEventMap = (LinkedHashMap<String,LinkedHashMap<String,String>>) webhookEvent;
String orderId = webhookEventMap.get("resource").get("id");
payPalCheckoutService.processOrder(orderId);
return Boolean.TRUE;
}
}
} catch (PayPalRESTException | InvalidKeyException | NoSuchAlgorithmException | SignatureException e) {
log.error(e.getMessage());
}
return Boolean.FALSE;
}
// Simple helper method to help you extract the headers from HttpServletRequest object.
private static Map<String, String> getHeadersInfo(HttpServletRequest request) {
Map<String, String> map = new HashMap<String, String>();
@SuppressWarnings("rawtypes")
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
// Simple helper method to fetch request data as a string from HttpServletRequest object.
private static String getBody(HttpServletRequest request) throws IOException {
String body;
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
log.info("回调参数 ===> {}", body);
return body;
}
}

View File

@@ -1,12 +1,14 @@
package com.ai.da.service.impl;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.utils.OrderNoUtils;
import com.ai.da.mapper.primary.OrderInfoMapper;
import com.ai.da.mapper.primary.ProductMapper;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.mapper.primary.entity.Product;
import com.ai.da.model.vo.AuthPrincipalVo;
import com.ai.da.service.OrderInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -25,9 +27,6 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
@Resource
private ProductMapper productMapper;
/*@Resource
private OrderInfoMapper orderInfoMapper;*/
@Override
public OrderInfo createOrderByProductId(Long productId, String paymentType) {
@@ -39,13 +38,16 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
//获取商品信息
Product product = productMapper.selectById(productId);
AuthPrincipalVo userHolder = UserContext.getUserHolder();
Long accountId = userHolder.getId();
//生成订单
orderInfo = new OrderInfo();
orderInfo.setAccountId(accountId);
orderInfo.setTitle(product.getTitle());
orderInfo.setOrderNo(OrderNoUtils.getOrderNo()); //订单号
orderInfo.setOrderNo(OrderNoUtils.getOrderNo()); //订单号 ??
orderInfo.setProductId(productId);
orderInfo.setTotalFee(product.getPrice()); //
orderInfo.setTotalFee(product.getPrice()); //
orderInfo.setOrderStatus(OrderStatusEnum.NOT_PAY.getType()); //未支付
orderInfo.setPaymentType(paymentType);
baseMapper.insert(orderInfo);
@@ -169,4 +171,20 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
OrderInfo orderInfo = baseMapper.selectOne(queryWrapper);
return orderInfo;
}
public List<OrderInfo> getOrderByAccountId(Long accountId){
QueryWrapper<OrderInfo> qw = new QueryWrapper<>();
qw.eq("account_id",accountId);
qw.orderByDesc("create_time");
return baseMapper.selectList(qw);
}
public void updateOrderNoById(Long id, String orderNo){
OrderInfo orderInfo = new OrderInfo();
orderInfo.setId(id);
orderInfo.setOrderNo(orderNo);
baseMapper.updateById(orderInfo);
}
}

View File

@@ -0,0 +1,476 @@
package com.ai.da.service.impl;
import cn.hutool.core.convert.Convert;
import com.ai.da.common.config.PayPalClient;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.PayPalCheckoutConstant;
import com.ai.da.common.enums.*;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.paypalRequest.AuthenticationRequest;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.mapper.primary.entity.RefundInfo;
import com.ai.da.service.OrderInfoService;
import com.ai.da.service.PayPalCheckoutService;
import com.ai.da.service.PaymentInfoService;
import com.ai.da.service.RefundInfoService;
import com.google.gson.Gson;
import com.paypal.http.HttpResponse;
import com.paypal.http.exceptions.SerializeException;
import com.paypal.http.serializer.Json;
import com.paypal.orders.*;
import com.paypal.payments.CapturesGetRequest;
import com.paypal.payments.CapturesRefundRequest;
import com.paypal.payments.RefundRequest;
import com.paypal.payments.RefundsGetRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import static com.ai.da.common.constant.PayPalCheckoutConstant.*;
@Slf4j
@Service
//@RefreshScope
public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
@Value("${paypal.receiver.email}")
private String receiverEmail;
@Value("${paypal.client-id}")
private String clientId;
@Value("${paypal.client-secret}")
private String clientSecret;
@Resource
private PayPalClient payPalClient;
@Resource
private OrderInfoService orderInfoService;
@Resource
private PaymentInfoService paymentInfoService;
@Resource
private RefundInfoService refundsInfoService;
@Resource
private RedisUtil redisUtil;
/**
* 创建订单的方法
*/
@Override
public HashMap<String, String> createOrder(Long productId,String returnUrl) throws SerializeException {
// 生成订单
log.info("生成订单");
OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId, PayTypeEnum.PAYPAL.getType());
OrdersCreateRequest request = new OrdersCreateRequest();
request.header("prefer","return=representation");
request.requestBody(buildRequestBody(String.valueOf(orderInfo.getTotalFee()),returnUrl));
HttpResponse<Order> response = null;
try {
response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
} catch (Exception e) {
log.error("调用paypal订单创建失败失败原因 ===> {}", e.getMessage());
throw new BusinessException("Order creation failed");
}
String approve = "";
assert response != null;
if (response.statusCode() == 201) {
log.info("Status Code = {}, Status = {}, OrderID = {}, Intent = {}", response.statusCode(), response.result().status(), response.result().id(), response.result().checkoutPaymentIntent());
for (LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
if(link.rel().equals("approve")) {
approve = link.href();
}
}
String totalAmount = response.result().purchaseUnits().get(0).amountWithBreakdown().currencyCode() + ":" + response.result().purchaseUnits().get(0).amountWithBreakdown().value();
log.info("Total Amount: {}", totalAmount);
String json= new JSONObject(new Json().serialize(response.result())).toString(4);
log.info("createOrder response body: {}", json);
}
String orderId = response.result().id();
orderInfoService.updateOrderNoById(orderInfo.getId(), orderId);
HashMap<String, String> returnData = new HashMap<>();
returnData.put("approve",approve);
returnData.put("orderNo",orderId);
// 需要返回地址和订单id
return returnData;
}
/**
* 生成订单主体信息
*/
private OrderRequest buildRequestBody(String price, String returnUrl) {
OrderRequest orderRequest = new OrderRequest();
orderRequest.checkoutPaymentIntent(CAPTURE);
ApplicationContext applicationContext = new ApplicationContext()
.brandName(BRANDNAME)
.landingPage(LANDINGPAGE)
.cancelUrl("https://www.example.com").returnUrl(returnUrl)
.userAction(USERACTION)
.shippingPreference(SHIPPINGPREFERENCE);
orderRequest.applicationContext(applicationContext);
List<PurchaseUnitRequest> purchaseUnitRequests = new ArrayList<PurchaseUnitRequest>();
PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest()
.amountWithBreakdown(new AmountWithBreakdown()
.amountBreakdown(new AmountBreakdown())
.currencyCode(CurrencyCodesEnum.HONG_KONG_DOLLAR.getCode())
.value(price));
purchaseUnitRequests.add(purchaseUnitRequest);
orderRequest.purchaseUnits(purchaseUnitRequests);
return orderRequest;
}
@Override
public String callback(@SuppressWarnings("rawtypes") Map map) {
log.info("paypal支付通知正在执行");
log.info("通知参数 ===> {}", map);
// log.info(map.toString());
String outTradeNo = (String)map.get("invoice");
String paymentStatus = (String)map.get("payment_status");
String amount = (String)map.get("mc_gross");
String currency = (String)map.get("mc_currency");
String paymentId = (String)map.get("txn_id");
String parentPaymentId = (String)map.get("parent_txn_id");
log.info("商家订单号 = {}", outTradeNo);
log.info("订单状态 = {}", paymentStatus);
log.info("金额 = {}", amount);
log.info("币种 = {}", currency);
log.info("流水号 = {}", paymentId);
log.info("父流水号 = {}", parentPaymentId);
if (!receiverEmail.equals(map.get("receiver_email"))) {
log.info("FAIL = 商户id错误, outTradeNo = {}", outTradeNo);
return "failure";
}
if("Completed".equals(paymentStatus)) {
//进行数据库操作
//
//
log.info("支付成功,状态为=COMPLETED");
return "success";
}
if("Refunded".equals(paymentStatus)) {
//进行数据库操作
//
//
log.info("退款成功");
return "success";
}
if("Pending".equals(paymentStatus) && StringUtils.isEmpty(parentPaymentId)) {
String pendingReason = String.valueOf(map.get("pending_reason"));
//进行数据库操作
//
//
log.info("订单支付成功,状态为=PENDING产生此状态的原因是 {}", pendingReason );
return "success";
}
if(StringUtils.isEmpty(parentPaymentId)) {
if(PayPalCheckoutConstant.PAYMENT_STATUS_REVERSED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_CANCELED_REVERSAL.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_DENIED.equals(paymentStatus)) {
String reasonCode = String.valueOf(map.get("reason_code"));
//进行数据库操作(状态修改)
//
//
log.info("订单异常,请尽快查看处理,状态为={},产生此状态的原因是 {} ", paymentStatus, reasonCode);
return PayPalCheckoutConstant.SUCCESS;
}
if(PayPalCheckoutConstant.PAYMENT_STATUS_EXPIRED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_CREATED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_FAILED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_PROCESSED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_VOIDED.equals(paymentStatus)) {
//进行数据库操作(状态修改)
//
//
log.info("其他订单状态,订单异常,请尽快查看处理, 状态={}", paymentStatus);
return PayPalCheckoutConstant.SUCCESS;
}
}
return "failure";
}
/**
* 查询订单信息
* @param orderNo
* @return
* @throws SerializeException
*/
public String queryOrder(String orderNo) throws SerializeException {
OrdersGetRequest request = new OrdersGetRequest(orderNo);
HttpResponse<Order> response = null;
try {
response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
} catch (Exception e) {
log.error("paypal订单查询失败失败原因 ===> {}",e.getMessage());
}
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Order id: " + response.result().id());
if(response.result().purchaseUnits().get(0).payments() != null) {
List<Capture> captures = response.result().purchaseUnits().get(0).payments().captures();
if(captures != null) {
for (Capture capture : captures) {
System.out.println("\t订单编号= " + capture.invoiceId() + "\tCapture Id= " + capture.id() + "\tCapture status= " + capture.status() + "\tCapture amount= " + capture.amount().currencyCode() + ":" + capture.amount().value());
}
}
List<Refund> refunds = response.result().purchaseUnits().get(0).payments().refunds();
if(refunds != null) {
for (Refund refund : refunds) {
System.out.println("\t售后编号= " + refund.invoiceId() + "\tRefund Id= " + refund.id() + "\tRefund status= " + refund.status() + "\tRefund amount= " + refund.amount().currencyCode() + ":" + refund.amount().value());
}
}
}
System.out.println("Links: ");
for (com.paypal.orders.LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
String json = new JSONObject(new Json().serialize(response.result())).toString(4);
System.out.println(json);
return null;
}
/**
* 用户授权支付成功,进行扣款操作
*/
public Order captureOrder(String orderId) {
OrdersCaptureRequest request = new OrdersCaptureRequest(orderId);
request.requestBody(new OrderRequest());
PayPalClient payPalClient = new PayPalClient();
HttpResponse<Order> response ;
try {
response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
} catch (Exception e) {
log.error("调用paypal扣款失败失败原因 ===> {}", e.getMessage() );
throw new BusinessException("Order deduction failed.");
}
log.info("Status Code = {}, Status = {}, OrderID = {}", response.statusCode(), response.result().status(), response.result().id());
for (LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
}
for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
for (Capture capture : purchaseUnit.payments().captures()) {
log.info("Capture id: {}", capture.id());
log.info("status: {}", capture.status());
log.info("invoice_id: {}", capture.invoiceId());
if("COMPLETED".equals(capture.status())) {
//进行数据库操作修改订单状态为已支付成功尽快发货配合回调和CapturesGet查询确定成功
log.info("支付成功,状态为=COMPLETED");
}
if("PENDING".equals(capture.status())) {
log.info("status_details: {}", capture.captureStatusDetails().reason());
String reason = "PENDING";
if(capture.captureStatusDetails() != null && capture.captureStatusDetails().reason() != null) {
reason = capture.captureStatusDetails().reason();
}
//进行数据库操作修改订单状态为已支付成功但触发了人工审核请审核通过后再发货配合回调和CapturesGet查询确定成功
log.info("支付成功,状态为=PENDING : {}", reason);
}
}
}
Payer buyer = response.result().payer();
log.info("Buyer Email Address: {}", buyer.email());
log.info("Buyer Name: {} {}", buyer.name().givenName(), buyer.name().surname());
// String jsonString = JSON.toJSONString(response.result());
String json = null;
try {
json = new JSONObject(new Json().serialize(response.result())).toString(4);
} catch (SerializeException e) {
log.warn("response序列化出错具体信息 ===> {}",e.getMessage());
}
log.info("captureOrder response body: {}", json);
// return Convert.toStr(new Json().serialize(response.result()));
return response.result();
}
/**
* 查询扣款
*/
public String queryCapture(String orderNo) throws IOException {
CapturesGetRequest request = new CapturesGetRequest("扣款id CaptureOrder生成");
HttpResponse<com.paypal.payments.Capture> response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Capture ids: " + response.result().id());
System.out.println("Links: ");
for (com.paypal.payments.LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
System.out.println(new JSONObject(new Json().serialize(response.result())).toString(4));
return null;
}
/**
* 申请退款
*/
public Boolean refundOrder(String orderId,String reason) throws IOException {
RefundInfo refundByOrderNo = refundsInfoService.createRefundByOrderNo(orderId, reason);
OrdersGetRequest ordersGetRequest = new OrdersGetRequest(orderId);
PayPalClient payPalClient = new PayPalClient();
HttpResponse<com.paypal.orders.Order> ordersGetResponse = null;
ordersGetRequest.authorization("Bearer " + getOAuth());
boolean result ;
try {
ordersGetResponse = payPalClient.client(MODE, clientId, clientSecret).execute(ordersGetRequest);
} catch (Exception e) {
log.error("调用paypal订单查询失败失败原因 ===> {}", e.getMessage());
throw new BusinessException("Order query failed");
}
String captureId = ordersGetResponse.result().purchaseUnits().get(0).payments().captures().get(0).id();
CapturesRefundRequest request = new CapturesRefundRequest(captureId);
request.authorization("Bearer " + getOAuth());
request.prefer("return=representation");
OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderId);
request.requestBody(buildRefundRequestBody(String.valueOf(orderInfo.getTotalFee()),reason));
HttpResponse<com.paypal.payments.Refund> response = null;
try {
response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
} catch (IOException e) {
log.error("调用paypal退款申请失败失败原因 {}", e.getMessage());
throw new BusinessException("Request for refund failed");
}
log.info("Status Code = {}, Status = {}, RefundID = {}", response.statusCode(), response.result().status(), response.result().id());
if("COMPLETED".equals(response.result().status())) {
//进行数据库操作,修改状态为已退款(配合回调和退款查询确定退款成功)
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_SUCCESS);
refundsInfoService.updateRefundForPayPal(
refundByOrderNo.getId(),
response.result().id(),
new Gson().toJson(response.result(), com.paypal.payments.Refund.class),
AliPayTradeStateEnum.REFUND_SUCCESS.getType()); //退款成功
log.info("退款成功");
result = Boolean.TRUE;
}else {
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_ABNORMAL);
//更新退款单
refundsInfoService.updateRefundForPayPal(
refundByOrderNo.getId(),
response.result().id(),
new Gson().toJson(response.result(), com.paypal.payments.Refund.class),
AliPayTradeStateEnum.REFUND_ERROR.getType()); //退款失败
result = Boolean.FALSE;
}
for (com.paypal.payments.LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
}
String json = new JSONObject(new Json().serialize(response.result())).toString(4);
log.info("refundOrder response body: {}", json);
return result;
}
public RefundRequest buildRefundRequestBody(String price, String reason) {
RefundRequest refundRequest = new RefundRequest();
com.paypal.payments.Money money = new com.paypal.payments.Money();
money.currencyCode(CurrencyCodesEnum.HONG_KONG_DOLLAR.getCode());
money.value(price);
refundRequest.amount(money);
// refundRequest.invoiceId("P2020052514440001");
refundRequest.noteToPayer(reason);
try {
log.info("refund order body : {}", Convert.toStr(new Json().serialize(refundRequest)));
} catch (SerializeException e) {
throw new RuntimeException(e);
}
return refundRequest;
}
/**
* 查询退款
*/
public String queryRefund(String orderNo) throws IOException {
RefundsGetRequest request = new RefundsGetRequest("退款id RefundOrder生成");
HttpResponse<com.paypal.payments.Refund> response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Refund Id: " + response.result().id());
System.out.println("Links: ");
for (com.paypal.payments.LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
System.out.println(new JSONObject(new Json().serialize(response.result())).toString(4));
return null;
}
public String getOAuth(){
// 1、判断缓存区是否有该token
Boolean hasKey = redisUtil.hasKey(PAYPAL_TOKEN_KEY);
if (hasKey){
return redisUtil.getFromString(PAYPAL_TOKEN_KEY);
// return "A21AAKnpozur9r9omqQ2ge5aXHBBdEERi5F8FIgNYOjhhO2N7rjmkz2irh2lScpBO3s3Cqukw3eZkpYZ4YWE7rIacjv7MHmow";
}
// 2、无或者过期重新获取token返回
AuthenticationRequest authenticationRequest = new AuthenticationRequest();
authenticationRequest.authorization(clientId,clientSecret);
try {
HttpResponse<HashMap> authResult = payPalClient.client(MODE, clientId, clientSecret).execute(authenticationRequest);
String accessToken = authResult.result().get("access_token").toString();
long expiresIn = Long.parseLong(authResult.result().get("expires_in").toString());
// 3、存redis
redisUtil.addToString(PAYPAL_TOKEN_KEY,accessToken,expiresIn);
return accessToken;
} catch (IOException e) {
log.error("获取paypal token失败失败原因 ===> {}", e.getMessage());
throw new BusinessException(e);
}
}
// 处理当前订单
public void processOrder(String orderId){
// 1、确定当前订单是否已经被扣款
OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderId);
if (orderInfo.getOrderStatus().equals(OrderStatusEnum.SUCCESS.getType())){
// 直接返回
return ;
}
// 发起扣款请求
Order capturedOrder = captureOrder(orderId);
// 业务处理
if (PayPalOrderStatusEnum.COMPLETED.getStatus().equals(capturedOrder.status())){
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfoForPayPal(capturedOrder);
}
}
}

View File

@@ -6,6 +6,7 @@ import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.service.PaymentInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.paypal.orders.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -70,8 +71,8 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
String tradeStatus = params.get("trade_status");
//交易金额
String totalAmount = params.get("total_amount");
int totalAmountInt = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
// int totalAmountInt = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
int totalAmountInt = new BigDecimal(totalAmount).intValue();
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setOrderNo(orderNo);
@@ -87,4 +88,27 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
baseMapper.insert(paymentInfo);
}
@Override
public void createPaymentInfoForPayPal(Order order) {
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();
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setOrderNo(order.id());
paymentInfo.setPaymentType(PayTypeEnum.PAYPAL.getType());
paymentInfo.setTransactionId(order.id());
paymentInfo.setTradeType("电脑网站支付");
paymentInfo.setTradeState(order.status());
paymentInfo.setPayerTotal(totalAmountInt);
Gson gson = new Gson();
String json = gson.toJson(order, Order.class);
paymentInfo.setContent(json);
baseMapper.insert(paymentInfo);
}
}

View File

@@ -40,8 +40,8 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
RefundInfo refundInfo = new RefundInfo();
refundInfo.setOrderNo(orderNo);//订单编号
refundInfo.setRefundNo(OrderNoUtils.getRefundNo());//退款单编号
refundInfo.setTotalFee(orderInfo.getTotalFee());//原订单金额()
refundInfo.setRefund(orderInfo.getTotalFee());//退款金额()
refundInfo.setTotalFee(orderInfo.getTotalFee());//原订单金额()
refundInfo.setRefund(orderInfo.getTotalFee());//退款金额()
refundInfo.setReason(reason);//退款原因
//保存退款订单
@@ -50,7 +50,6 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
return refundInfo;
}
/**
* 记录退款记录
* @param content
@@ -68,7 +67,6 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
//设置要修改的字段
RefundInfo refundInfo = new RefundInfo();
refundInfo.setRefundId(resultMap.get("refund_id"));//微信支付退款单号
//查询退款和申请退款中的返回参数
@@ -120,8 +118,8 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
refundInfo.setOrderNo(orderNo);//订单编号
refundInfo.setRefundNo(OrderNoUtils.getRefundNo());//退款单编号
refundInfo.setTotalFee(orderInfo.getTotalFee());//原订单金额()
refundInfo.setRefund(orderInfo.getTotalFee());//退款金额()
refundInfo.setTotalFee(orderInfo.getTotalFee());//原订单金额()
refundInfo.setRefund(orderInfo.getTotalFee());//退款金额()
refundInfo.setReason(reason);//退款原因
//保存退款订单
@@ -153,4 +151,23 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
}
@Override
public void updateRefundForPayPal(Long id, String refundId,String content, String refundStatus) {
//根据退款单编号修改退款单
// QueryWrapper<RefundInfo> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("order_no", orderNo);
//设置要修改的字段
RefundInfo refundInfo = new RefundInfo();
refundInfo.setId(id);
refundInfo.setRefundNo(null);
refundInfo.setRefundId(refundId);
refundInfo.setRefundStatus(refundStatus);//退款状态
refundInfo.setContentReturn(content);//将全部响应结果存入数据库的content字段
//更新退款单
// baseMapper.update(refundInfo, queryWrapper);
baseMapper.updateById(refundInfo);
}
}