feat(payment): 实现订阅订单微信Native支付及二维码生成功能
- 新增 MyQrCodeUtil.generateBase64 生成二维码Base64字符串用于前端展示,无需写文件 - SubscriptionOrderPayResult新增支付二维码图片(qrImage)和订单状态(orderStatus)字段 - TenantSubscriptionOrderController新增创建订阅订单并生成微信Native支付二维码接口 - 新增查询订阅订单支付状态接口支持前端轮询支付结果 - TenantSubscriptionOrderController内部封装从订单构造价格结果方法,保证价格数据一致 - WxNativePayController支持订阅订单号支付,保留调用方传入订单号,避免随机生成 - 微信Native支付异步通知接口增加验签解密,解析交易结果后更新订阅订单支付状态与激活流程 - 异步通知中新增日志支持,完善异常捕获及幂等处理,保障回调安全和准确 - 依赖注入新增订阅订单及套餐服务,优化代码结构和职责分离
This commit is contained in:
@@ -7,8 +7,10 @@ import cn.hutool.extra.qrcode.QrConfig;
|
|||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import static com.gxwebsoft.common.core.constants.QRCodeConstants.*;
|
import static com.gxwebsoft.common.core.constants.QRCodeConstants.*;
|
||||||
@@ -77,4 +79,17 @@ public class MyQrCodeUtil {
|
|||||||
QrCodeUtil.generate(content, config, FileUtil.file(filePath));
|
QrCodeUtil.generate(content, config, FileUtil.file(filePath));
|
||||||
return qrcodeUrl;
|
return qrcodeUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成二维码并返回 base64(data:image/png;base64,...),用于前端直接展示,无需写文件
|
||||||
|
* @param content 二维码内容(如微信支付 code_url)
|
||||||
|
* @return base64 图片字符串
|
||||||
|
*/
|
||||||
|
public static String generateBase64(String content) throws IOException {
|
||||||
|
QrConfig config = new QrConfig(300, 300);
|
||||||
|
config.setMargin(1);
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
QrCodeUtil.generate(content, config, "PNG", out);
|
||||||
|
return "data:image/png;base64," + Base64.getEncoder().encodeToString(out.toByteArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+107
-1
@@ -4,17 +4,25 @@ import cn.hutool.core.util.IdUtil;
|
|||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.gxwebsoft.common.core.Constants;
|
import com.gxwebsoft.common.core.Constants;
|
||||||
|
import com.gxwebsoft.common.core.utils.MyQrCodeUtil;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import com.gxwebsoft.common.system.entity.Order;
|
import com.gxwebsoft.common.system.entity.Order;
|
||||||
|
import com.gxwebsoft.common.system.entity.TenantPackage;
|
||||||
|
import com.gxwebsoft.common.system.entity.TenantSubscriptionOrder;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.common.system.param.SubscriptionOrderParam;
|
import com.gxwebsoft.common.system.param.SubscriptionOrderParam;
|
||||||
import com.gxwebsoft.common.system.result.SubscriptionOrderCreateResult;
|
import com.gxwebsoft.common.system.result.SubscriptionOrderCreateResult;
|
||||||
import com.gxwebsoft.common.system.result.SubscriptionOrderPayResult;
|
import com.gxwebsoft.common.system.result.SubscriptionOrderPayResult;
|
||||||
import com.gxwebsoft.common.system.result.SubscriptionPriceResult;
|
import com.gxwebsoft.common.system.result.SubscriptionPriceResult;
|
||||||
import com.gxwebsoft.common.system.service.SettingService;
|
import com.gxwebsoft.common.system.service.SettingService;
|
||||||
|
import com.gxwebsoft.common.system.service.TenantPackageService;
|
||||||
|
import com.gxwebsoft.common.system.service.TenantSubscriptionOrderService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -29,6 +37,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* 订阅订单接口
|
* 订阅订单接口
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Tag(name = "订阅订单")
|
@Tag(name = "订阅订单")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/system/subscription-order")
|
@RequestMapping("/api/system/subscription-order")
|
||||||
@@ -38,6 +47,10 @@ public class TenantSubscriptionOrderController extends BaseController {
|
|||||||
private SettingService settingService;
|
private SettingService settingService;
|
||||||
@Resource
|
@Resource
|
||||||
private WxNativePayController wxNativePayController;
|
private WxNativePayController wxNativePayController;
|
||||||
|
@Resource
|
||||||
|
private TenantSubscriptionOrderService subscriptionOrderService;
|
||||||
|
@Resource
|
||||||
|
private TenantPackageService tenantPackageService;
|
||||||
|
|
||||||
@Operation(summary = "计算订阅订单价格")
|
@Operation(summary = "计算订阅订单价格")
|
||||||
@PostMapping("/calculate-price")
|
@PostMapping("/calculate-price")
|
||||||
@@ -88,7 +101,6 @@ public class TenantSubscriptionOrderController extends BaseController {
|
|||||||
|
|
||||||
JSONObject config = loadSubscriptionConfig();
|
JSONObject config = loadSubscriptionConfig();
|
||||||
final SubscriptionPriceResult price = buildPriceResult(param, config);
|
final SubscriptionPriceResult price = buildPriceResult(param, config);
|
||||||
price.setPayPrice(new BigDecimal("0.01"));
|
|
||||||
if (price.getPayPrice() == null || price.getPayPrice().compareTo(BigDecimal.ZERO) <= 0) {
|
if (price.getPayPrice() == null || price.getPayPrice().compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
return fail("支付金额必须大于0", null);
|
return fail("支付金额必须大于0", null);
|
||||||
}
|
}
|
||||||
@@ -114,6 +126,100 @@ public class TenantSubscriptionOrderController extends BaseController {
|
|||||||
return success(result);
|
return success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建官网开通订单并直接生成微信Native支付二维码。
|
||||||
|
* 用于 /register 最后一步「支付开通」:建真实订单(TenantSubscriptionOrder)→ 调 Native 预下单
|
||||||
|
* (沿用已落库的 orderNo,保证回调查询一致)→ 将 code_url 编码为 base64 二维码返回。
|
||||||
|
*/
|
||||||
|
@Operation(summary = "创建开通订单并生成微信Native支付二维码")
|
||||||
|
@PostMapping("/create-and-pay")
|
||||||
|
public ApiResult<SubscriptionOrderPayResult> createAndPay(@RequestBody SubscriptionOrderParam param) {
|
||||||
|
final User loginUser = getLoginUser();
|
||||||
|
if (loginUser == null) {
|
||||||
|
return fail("请先登录", null);
|
||||||
|
}
|
||||||
|
Integer packageId = param.getPackageId();
|
||||||
|
if (packageId == null) {
|
||||||
|
// 默认基础版(version=10)
|
||||||
|
TenantPackage basic = tenantPackageService.getByVersion(10);
|
||||||
|
if (basic == null) {
|
||||||
|
return fail("未找到基础版套餐,请联系管理员配置", null);
|
||||||
|
}
|
||||||
|
packageId = basic.getPackageId();
|
||||||
|
}
|
||||||
|
Integer payType = param.getPayType() != null ? param.getPayType() : 12; // 默认年付
|
||||||
|
|
||||||
|
// 1) 落库真实订单(状态 0 待支付)
|
||||||
|
TenantSubscriptionOrder order;
|
||||||
|
try {
|
||||||
|
order = subscriptionOrderService.createOrder(packageId, payType, loginUser.getTenantId(), loginUser.getUserId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return fail(e.getMessage(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) 用已落库的订单号生成支付二维码(关键:不能重新生成随机号)
|
||||||
|
Order payOrder = new Order();
|
||||||
|
payOrder.setOrderNo(order.getOrderNo());
|
||||||
|
payOrder.setPayPrice(order.getActualPrice());
|
||||||
|
payOrder.setTotalPrice(order.getActualPrice());
|
||||||
|
payOrder.setComments("官网基础版开通-" + order.getOrderNo());
|
||||||
|
payOrder.setPayType(payType);
|
||||||
|
payOrder.setUserId(loginUser.getUserId());
|
||||||
|
payOrder.setTenantId(loginUser.getTenantId());
|
||||||
|
|
||||||
|
ApiResult<?> payResp = wxNativePayController.getCodeUrl(payOrder);
|
||||||
|
if (payResp.getCode() == null || !payResp.getCode().equals(Constants.RESULT_OK_CODE)) {
|
||||||
|
return fail(payResp.getMessage(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
String codeUrl = String.valueOf(payResp.getData());
|
||||||
|
String qrImage = null;
|
||||||
|
try {
|
||||||
|
qrImage = MyQrCodeUtil.generateBase64(codeUrl);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("生成支付二维码图片失败,前端将改用 codeUrl 渲染: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscriptionOrderPayResult result = new SubscriptionOrderPayResult();
|
||||||
|
result.setOrderNo(order.getOrderNo());
|
||||||
|
result.setCodeUrl(codeUrl);
|
||||||
|
result.setQrImage(qrImage);
|
||||||
|
result.setOrderStatus(order.getOrderStatus());
|
||||||
|
result.setPrice(buildPriceFromOrder(order));
|
||||||
|
return success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮询订单支付状态(前端支付页轮询用)。
|
||||||
|
* 状态由微信回调(wx-native-pay/notify)在服务端验签解密后翻转:0待支付→1已支付→2已激活。
|
||||||
|
*/
|
||||||
|
@Operation(summary = "查询开通订单支付状态")
|
||||||
|
@GetMapping("/status/{orderNo}")
|
||||||
|
public ApiResult<SubscriptionOrderPayResult> getOrderStatus(@PathVariable("orderNo") String orderNo) {
|
||||||
|
TenantSubscriptionOrder order = subscriptionOrderService.getByOrderNo(orderNo);
|
||||||
|
if (order == null) {
|
||||||
|
return fail("订单不存在", null);
|
||||||
|
}
|
||||||
|
SubscriptionOrderPayResult result = new SubscriptionOrderPayResult();
|
||||||
|
result.setOrderNo(order.getOrderNo());
|
||||||
|
result.setOrderStatus(order.getOrderStatus());
|
||||||
|
result.setPrice(buildPriceFromOrder(order));
|
||||||
|
return success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 由已落库订单构造前端所需的价格结果
|
||||||
|
*/
|
||||||
|
private SubscriptionPriceResult buildPriceFromOrder(TenantSubscriptionOrder order) {
|
||||||
|
SubscriptionPriceResult price = new SubscriptionPriceResult();
|
||||||
|
price.setPackageId(order.getPackageId());
|
||||||
|
price.setOriginalPrice(order.getOriginalPrice() == null ? null : order.getOriginalPrice().setScale(2, RoundingMode.HALF_UP));
|
||||||
|
price.setTotalPrice(order.getOriginalPrice() == null ? null : order.getOriginalPrice().setScale(2, RoundingMode.HALF_UP));
|
||||||
|
price.setPayPrice(order.getActualPrice() == null ? null : order.getActualPrice().setScale(2, RoundingMode.HALF_UP));
|
||||||
|
price.setDiscountAmount(order.getDiscountPrice() == null ? null : order.getDiscountPrice().setScale(2, RoundingMode.HALF_UP));
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从配置表读取订阅套餐配置(尝试多种key以兼容历史数据)
|
* 从配置表读取订阅套餐配置(尝试多种key以兼容历史数据)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -12,15 +12,22 @@ import com.gxwebsoft.common.core.web.ApiResult;
|
|||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.common.system.entity.Order;
|
import com.gxwebsoft.common.system.entity.Order;
|
||||||
import com.gxwebsoft.common.system.entity.Payment;
|
import com.gxwebsoft.common.system.entity.Payment;
|
||||||
|
import com.gxwebsoft.common.system.entity.TenantSubscriptionOrder;
|
||||||
import com.gxwebsoft.common.system.param.SettingParam;
|
import com.gxwebsoft.common.system.param.SettingParam;
|
||||||
import com.gxwebsoft.common.system.service.OrderService;
|
import com.gxwebsoft.common.system.service.OrderService;
|
||||||
import com.gxwebsoft.common.system.service.SettingService;
|
import com.gxwebsoft.common.system.service.SettingService;
|
||||||
|
import com.gxwebsoft.common.system.service.TenantSubscriptionOrderService;
|
||||||
import com.wechat.pay.java.core.Config;
|
import com.wechat.pay.java.core.Config;
|
||||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||||
|
import com.wechat.pay.java.core.notification.NotificationConfig;
|
||||||
|
import com.wechat.pay.java.core.notification.NotificationParser;
|
||||||
|
import com.wechat.pay.java.core.notification.RSANotificationConfig;
|
||||||
|
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
|
||||||
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
|
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
|
||||||
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
|
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
|
||||||
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
|
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
|
||||||
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
|
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -62,6 +69,8 @@ public class WxNativePayController extends BaseController {
|
|||||||
private CertificateService certificateService;
|
private CertificateService certificateService;
|
||||||
@Resource
|
@Resource
|
||||||
private CertificateProperties certificateProperties;
|
private CertificateProperties certificateProperties;
|
||||||
|
@Resource
|
||||||
|
private TenantSubscriptionOrderService subscriptionOrderService;
|
||||||
|
|
||||||
|
|
||||||
@Operation(summary = "生成付款码")
|
@Operation(summary = "生成付款码")
|
||||||
@@ -92,7 +101,10 @@ public class WxNativePayController extends BaseController {
|
|||||||
PrepayRequest request = new PrepayRequest();
|
PrepayRequest request = new PrepayRequest();
|
||||||
// 计算金额
|
// 计算金额
|
||||||
order.setMoney(new BigDecimal(order.getPayPrice().toString()));
|
order.setMoney(new BigDecimal(order.getPayPrice().toString()));
|
||||||
|
// 若调用方已指定订单号(如订阅订单),则保留以关联回调查询;否则生成随机号
|
||||||
|
if (order.getOrderNo() == null || order.getOrderNo().trim().isEmpty()) {
|
||||||
order.setOrderNo(CommonUtil.createOrderNo());
|
order.setOrderNo(CommonUtil.createOrderNo());
|
||||||
|
}
|
||||||
BigDecimal decimal = order.getMoney();
|
BigDecimal decimal = order.getMoney();
|
||||||
final BigDecimal multiply = decimal.multiply(new BigDecimal(100));
|
final BigDecimal multiply = decimal.multiply(new BigDecimal(100));
|
||||||
// 将 BigDecimal 转换为 Integer
|
// 将 BigDecimal 转换为 Integer
|
||||||
@@ -229,20 +241,67 @@ public class WxNativePayController extends BaseController {
|
|||||||
@Schema(description = "异步通知")
|
@Schema(description = "异步通知")
|
||||||
@PostMapping("/notify/{tenantId}")
|
@PostMapping("/notify/{tenantId}")
|
||||||
public String wxNotify(@RequestHeader Map<String, String> header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) {
|
public String wxNotify(@RequestHeader Map<String, String> header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) {
|
||||||
System.out.println("异步通知*************** = ");
|
log.info("微信Native支付异步通知, tenantId={}", tenantId);
|
||||||
System.out.println("request header = " + header);
|
try {
|
||||||
System.out.println("request body = " + body);
|
// 1) 获取支付配置用于验签/解密(优先 Native 支付配置,兜底会员支付配置,再兜底静态测试商户)
|
||||||
System.out.println("tenantId = " + tenantId);
|
Payment payment = redisUtil.get("Payment:wxPay:".concat(tenantId.toString()), Payment.class);
|
||||||
|
if (payment == null) {
|
||||||
|
payment = redisUtil.get("Payment:1:".concat(tenantId.toString()), Payment.class);
|
||||||
|
}
|
||||||
|
String apiV3Key;
|
||||||
|
String apiclientCert;
|
||||||
|
if (payment != null) {
|
||||||
|
apiV3Key = payment.getApiKey();
|
||||||
|
apiclientCert = config.getUploadPath().concat("/file").concat(payment.getApiclientCert());
|
||||||
|
} else {
|
||||||
|
apiV3Key = WxNativePayController.apiV3Key;
|
||||||
|
apiclientCert = config.getUploadPath().concat("/file/wxpay/apiclient_cert.pem");
|
||||||
|
}
|
||||||
|
|
||||||
// 推送微信官方支付结果(携带租户ID的POST请求)
|
com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder()
|
||||||
// final String string = requestUtil.pushWxPayNotify(transaction, payment);
|
.serialNumber(header.get("wechatpay-serial"))
|
||||||
|
.nonce(header.get("wechatpay-nonce"))
|
||||||
|
.signature(header.get("wechatpay-signature"))
|
||||||
|
.timestamp(header.get("wechatpay-timestamp"))
|
||||||
|
.body(body)
|
||||||
|
.build();
|
||||||
|
|
||||||
// 获取支付配置信息用于解密
|
NotificationConfig notificationConfig = new RSANotificationConfig.Builder()
|
||||||
final SettingParam param = new SettingParam();
|
.apiV3Key(apiV3Key)
|
||||||
param.setSettingKey("payment");
|
.certificatesFromPath(apiclientCert)
|
||||||
param.setTenantId(tenantId);
|
.build();
|
||||||
final String uploadPath = config.getUploadPath(); // 服务器本地路径
|
|
||||||
|
|
||||||
|
NotificationParser parser = new NotificationParser(notificationConfig);
|
||||||
|
|
||||||
|
// 2) 验签 + 解密,拿到 Transaction
|
||||||
|
Transaction transaction = parser.parse(requestParam, Transaction.class);
|
||||||
|
if (transaction == null) {
|
||||||
|
log.warn("微信Native支付回调解析失败: transaction 为空");
|
||||||
|
return "fail";
|
||||||
|
}
|
||||||
|
if (transaction.getTradeState() != Transaction.TradeStateEnum.SUCCESS) {
|
||||||
|
log.info("微信Native支付回调:交易状态非成功, state={}", transaction.getTradeState());
|
||||||
|
return "SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) 按 out_trade_no(=订阅订单号)翻订单状态:0待支付→1已支付→2已激活
|
||||||
|
String outTradeNo = transaction.getOutTradeNo();
|
||||||
|
TenantSubscriptionOrder order = subscriptionOrderService.getByOrderNo(outTradeNo);
|
||||||
|
if (order == null) {
|
||||||
|
log.warn("微信Native支付回调:订单不存在, outTradeNo={}", outTradeNo);
|
||||||
|
return "SUCCESS";
|
||||||
|
}
|
||||||
|
if (order.getOrderStatus() == 0) {
|
||||||
|
subscriptionOrderService.payOrder(outTradeNo, "wechat", transaction.getTransactionId());
|
||||||
|
subscriptionOrderService.activateOrder(outTradeNo);
|
||||||
|
log.info("订阅订单支付成功并激活, orderNo={}, tenantId={}", outTradeNo, tenantId);
|
||||||
|
} else {
|
||||||
|
log.info("订阅订单已处理(幂等),orderNo={}, status={}", outTradeNo, order.getOrderStatus());
|
||||||
|
}
|
||||||
|
return "SUCCESS";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("微信Native支付回调处理失败: {}", e.getMessage(), e);
|
||||||
return "fail";
|
return "fail";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ public class SubscriptionOrderPayResult {
|
|||||||
@Schema(description = "支付二维码链接")
|
@Schema(description = "支付二维码链接")
|
||||||
private String codeUrl;
|
private String codeUrl;
|
||||||
|
|
||||||
|
@Schema(description = "支付二维码图片(base64),前端可直接展示")
|
||||||
|
private String qrImage;
|
||||||
|
|
||||||
|
@Schema(description = "订单状态 0待支付 1已支付 2已激活 3已取消")
|
||||||
|
private Integer orderStatus;
|
||||||
|
|
||||||
@Schema(description = "价格信息")
|
@Schema(description = "价格信息")
|
||||||
private SubscriptionPriceResult price;
|
private SubscriptionPriceResult price;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user