|
@@ -16,6 +16,7 @@ import cn.kdan.cloud.pdf.office.common.enums.ExceptionEnum;
|
|
|
import cn.kdan.cloud.pdf.office.common.enums.payment.SubscriptionTypeEnum;
|
|
|
import cn.kdan.cloud.pdf.office.common.exception.BackendRuntimeException;
|
|
|
import cn.kdan.cloud.pdf.office.common.utils.CommonUtils;
|
|
|
+import cn.kdan.cloud.pdf.office.common.utils.MyDateUtils;
|
|
|
import cn.kdan.cloud.pdf.office.common.vo.UserInfoVO;
|
|
|
import cn.kdan.cloud.pdf.office.common.vo.UserVO;
|
|
|
import cn.kdan.cloud.pdf.office.payment.client.AppStoreClient;
|
|
@@ -24,14 +25,25 @@ import cn.kdan.cloud.pdf.office.payment.service.AppStoreService;
|
|
|
import cn.kdan.cloud.pdf.office.payment.service.OrderService;
|
|
|
import cn.kdan.cloud.pdf.office.payment.service.RestorePurchaseLogsService;
|
|
|
import cn.kdan.cloud.pdf.office.payment.service.SubscriptionsService;
|
|
|
+import cn.kdan.cloud.pdf.office.payment.utils.TemplatesUtil;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.http.HttpEntity;
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.util.LinkedMultiValueMap;
|
|
|
+import org.springframework.util.MultiValueMap;
|
|
|
+import org.springframework.util.StreamUtils;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
|
|
|
+import java.io.IOException;
|
|
|
import java.math.BigDecimal;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
import java.util.*;
|
|
|
|
|
@@ -58,6 +70,12 @@ public class AppStoreServiceImpl implements AppStoreService {
|
|
|
|
|
|
|
|
|
private final EmailApi emailApi;
|
|
|
+ private AppStoreProperties properties;
|
|
|
+ private static final RestTemplate restTemplate = new RestTemplate();
|
|
|
+
|
|
|
+ @Value("${htmlToPdfUrl:http://139.196.160.101:3060/api/get-invoice}")
|
|
|
+ private String htmlToPdfUrl;
|
|
|
+
|
|
|
|
|
|
|
|
|
@Transactional
|
|
@@ -75,6 +93,7 @@ public class AppStoreServiceImpl implements AppStoreService {
|
|
|
String transactionId = appTransaction.getTransactionId();
|
|
|
String originalTransactionId = appTransaction.getOriginalTransactionId();
|
|
|
BigDecimal price = BigDecimal.valueOf(appTransaction.getPrice()/1000);
|
|
|
+ String currency = appTransaction.getCurrency();
|
|
|
OrdersVO orderByThirdOrderNo = orderService.getOrderByThirdOrderNo(transactionId);
|
|
|
if (null != orderByThirdOrderNo) {
|
|
|
throw new BackendRuntimeException(ExceptionEnum.EXCEPTION_TICKET_HAS_BEEN_CREATED);
|
|
@@ -103,11 +122,13 @@ public class AppStoreServiceImpl implements AppStoreService {
|
|
|
createSubscription.setPrice(price);
|
|
|
createSubscription.setPayTime(0);
|
|
|
subscriptionsService.createSubscription(createSubscription);
|
|
|
+ String readeNo = MyDateUtils.getTimeStamp() + "-" + product.getId() + "-" + (int) ((Math.random() * 9 + 1) * 1000);
|
|
|
// 更新订单
|
|
|
CreateOrderManualDTO orderManualDTO = CreateOrderManualDTO.builder()
|
|
|
.thirdTradeNo(originalTransactionId)
|
|
|
.thirdOrderNo(transactionId)
|
|
|
- .price(price)
|
|
|
+ .price(product.getPrice())
|
|
|
+ .reducedPrice(price)
|
|
|
.subscriptionId(subscriptionId)
|
|
|
.userId(equityVerificationDTO.getUserId())
|
|
|
.email(userVO.getEmail())
|
|
@@ -118,16 +139,19 @@ public class AppStoreServiceImpl implements AppStoreService {
|
|
|
.paymentModel(2)
|
|
|
.subscriptionType(1)
|
|
|
.payNumber(1)
|
|
|
+ .tradeNo(readeNo)
|
|
|
+ .invoiceNo(MyDateUtils.getTimeStamp() + (int) ((Math.random() * 9 + 1) * 1000))
|
|
|
.result(appTransaction.toString())
|
|
|
+ .currency(currency)
|
|
|
.build();
|
|
|
orderService.createOrderManual(orderManualDTO);
|
|
|
- sendBuyEmail(product, userVO);
|
|
|
UserInfoVO result = userApi.getInfoById(equityVerificationDTO.getUserId()).getResult();
|
|
|
result.setDigestPassword(null);
|
|
|
+ sendBuyEmail(product, userVO,orderManualDTO);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- private void sendBuyEmail(ProductVO product, UserVO userVO) {
|
|
|
+ private void sendBuyEmail(ProductVO product, UserVO userVO,CreateOrderManualDTO orderManualDTO) {
|
|
|
if(product.getCode().contains("upgrade")){
|
|
|
EmailSendBO bo = new EmailSendBO();
|
|
|
bo.setToEmail(userVO.getEmail());
|
|
@@ -144,13 +168,22 @@ public class AppStoreServiceImpl implements AppStoreService {
|
|
|
// 格式化当前日期
|
|
|
String formattedDate = sdf.format(date);
|
|
|
contentMap.put("@date@",formattedDate);
|
|
|
- String price = ObjectUtils.isNotEmpty(product.getDisplayPrice())? product.getDisplayPrice().toString() : product.getPrice().toString();
|
|
|
-
|
|
|
+ String price;
|
|
|
+ BigDecimal renewPrice;
|
|
|
+ if(orderManualDTO.getCurrency().equals("USD")){
|
|
|
+ price = ObjectUtils.isNotEmpty(product.getDisplayPrice())? product.getDisplayPrice().toString() : product.getPrice().toString();
|
|
|
+ renewPrice = product.getPrice();
|
|
|
+ }else{
|
|
|
+ price = ObjectUtils.isNotEmpty(product.getCnyDisplayPrice())? product.getCnyDisplayPrice().toString() : product.getCnyPrice().toString();
|
|
|
+ renewPrice = product.getCnyPrice();
|
|
|
+ }
|
|
|
contentMap.put("@payPrice@", price);
|
|
|
- contentMap.put("@renewPrice@", product.getPrice().toString());
|
|
|
+ contentMap.put("@renewPrice@", renewPrice.toString());
|
|
|
bo.setSendTitleContent(titleMap);
|
|
|
//设置内容
|
|
|
bo.setSendContent(contentMap);
|
|
|
+ String file = sendInvoice(userVO.getEmail(),formattedDate,orderManualDTO.getTradeNo(),orderManualDTO.getInvoiceNo(),renewPrice,orderManualDTO.getPrice());
|
|
|
+ bo.setFileUrl(file);
|
|
|
emailApi.sendEmail(bo);
|
|
|
}else{
|
|
|
EmailSendBO bo = new EmailSendBO();
|
|
@@ -168,15 +201,62 @@ public class AppStoreServiceImpl implements AppStoreService {
|
|
|
// 格式化当前日期
|
|
|
String formattedDate = sdf.format(date);
|
|
|
contentMap.put("@date@",formattedDate);
|
|
|
- String price = ObjectUtils.isNotEmpty(product.getDisplayPrice())? product.getDisplayPrice().toString() : product.getPrice().toString();
|
|
|
+ String price;
|
|
|
+ BigDecimal renewPrice;
|
|
|
+ if(orderManualDTO.getCurrency().equals("USD")){
|
|
|
+ price = ObjectUtils.isNotEmpty(product.getDisplayPrice())? product.getDisplayPrice().toString() : product.getPrice().toString();
|
|
|
+ renewPrice = product.getPrice();
|
|
|
+ }else{
|
|
|
+ price = ObjectUtils.isNotEmpty(product.getCnyDisplayPrice())? product.getCnyDisplayPrice().toString() : product.getCnyPrice().toString();
|
|
|
+ renewPrice = product.getCnyPrice();
|
|
|
+ }
|
|
|
contentMap.put("@payPrice@", price);
|
|
|
bo.setSendTitleContent(titleMap);
|
|
|
//设置内容
|
|
|
bo.setSendContent(contentMap);
|
|
|
+ String file = sendInvoice(userVO.getEmail(),formattedDate,orderManualDTO.getTradeNo(),orderManualDTO.getInvoiceNo(),renewPrice,orderManualDTO.getPrice());
|
|
|
+ bo.setFileUrl(file);
|
|
|
emailApi.sendEmail(bo);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ //生成发票
|
|
|
+ private String sendInvoice(String email, String purchasedDate,String tradeNo,String invoiceNumber,BigDecimal unitPrice,BigDecimal amount) {
|
|
|
+ String invoiceHtml = null;
|
|
|
+ try {
|
|
|
+ invoiceHtml = new String(StreamUtils.copyToByteArray(this.getClass().getClassLoader().getResourceAsStream("templates/invoice_member.html")), StandardCharsets.UTF_8);
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error(e.getMessage());
|
|
|
+ }
|
|
|
+ Map<String, Object> map = new HashMap<>();
|
|
|
+ map.put("Name", email);
|
|
|
+ map.put("email", email);
|
|
|
+ map.put("purchasedDate", purchasedDate);
|
|
|
+ map.put("orderNumber",tradeNo);
|
|
|
+ map.put("invoiceDate", purchasedDate);
|
|
|
+ map.put("invoiceNumber", invoiceNumber);
|
|
|
+ map.put("quantity", 1);
|
|
|
+ map.put("unitPrice", unitPrice);
|
|
|
+ BigDecimal discountAmount = unitPrice.subtract(amount);
|
|
|
+ map.put("discount", discountAmount);
|
|
|
+ map.put("amount", amount);
|
|
|
+ map.put("total", amount);
|
|
|
+ log.info("发票html内容填充map:{}", map);
|
|
|
+ // 生成替换内容后的发票
|
|
|
+ String htmlStr = TemplatesUtil.replaceStringUsingFreeMarker(invoiceHtml, map);
|
|
|
+ HttpHeaders headers = new HttpHeaders();
|
|
|
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
|
+ // 封装表单数据
|
|
|
+ MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
|
|
|
+ paramMap.add("html", htmlStr);
|
|
|
+ HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(paramMap, headers);
|
|
|
+ // 发送POST请求,html转pdf
|
|
|
+ Map postForObjectMap = restTemplate.postForObject(htmlToPdfUrl, request, Map.class);
|
|
|
+ String invoiceFileUrl = postForObjectMap.get("url").toString();
|
|
|
+ log.info("发票文件地址:{}", invoiceFileUrl);
|
|
|
+ return invoiceFileUrl;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 票据验证
|
|
|
*
|
|
@@ -185,11 +265,17 @@ public class AppStoreServiceImpl implements AppStoreService {
|
|
|
private AppTransaction appStoreReceiptVerify(String transactionId, String applePayProductId,String appBundleId) {
|
|
|
// 验证票据信息
|
|
|
AppTransaction transaction = appStoreClient.fetchFirstAppTransaction(transactionId,appBundleId);
|
|
|
- if(Objects.isNull(transaction)){
|
|
|
- log.error("验证苹果交易失败,transactionId:{}",transactionId);
|
|
|
+ // 如果主环境交易记录为null,尝试获取沙箱环境交易记录
|
|
|
+ if (Objects.isNull(transaction) && "false".equals(properties.getIsSandbox())) {
|
|
|
+ transaction = appStoreClient.fetchSandboxFirstAppTransaction(transactionId, appBundleId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果最终transaction仍为null,抛出验证失败异常
|
|
|
+ if (Objects.isNull(transaction)) {
|
|
|
+ log.error("验证苹果交易失败,transactionId:{}", transactionId);
|
|
|
throw new BackendRuntimeException(ExceptionEnum.EXCEPTION_MSG_APP_STORE_TRANSACTION_ID_VALIDATION_FAILED);
|
|
|
}
|
|
|
- log.info("票据信息:{}", transaction.toString());
|
|
|
+ log.info("苹果票据信息:{}", transaction.toString());
|
|
|
// 检查支付状态等
|
|
|
if (transaction.getProductId().equals(applePayProductId)) {
|
|
|
Date expiresDateMs = transaction.getExpiresDate();
|