初始化
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
import '../../routers/jump_router.dart';
|
||||
import '../../tools_base/loading/loading_alert_widget.dart';
|
||||
import '../../tools_base/net/base_resp_bean.dart';
|
||||
import '../../tools_base/net/net_code.dart';
|
||||
import '../api_service/buy_service.dart';
|
||||
|
||||
/// 下单商品类型,对应 /product/buy 的 productType 字段。
|
||||
/// 只收单品下单用到的类型;VIP 卡的 productType 由服务端随卡下发(新人卡 5、预售卡 21 等),
|
||||
/// 走 [PayManager.buyVip] 的 int
|
||||
enum ProductType {
|
||||
/// 1 单个作品:金币视频、黄游(SEED_LINK)、社区帖子
|
||||
media(1),
|
||||
|
||||
/// 19 整本/整部:漫画整本、动漫整部、短剧(买单集时靠 contentID 指定哪一集)
|
||||
mediaAll(19),
|
||||
|
||||
/// 25 漫画单集
|
||||
mediaChapter(25),
|
||||
|
||||
/// 101 群聊
|
||||
group(101);
|
||||
|
||||
const ProductType(this.value);
|
||||
|
||||
/// 传给接口的原始值
|
||||
final int value;
|
||||
}
|
||||
|
||||
/// 支付管理:统一下单流程。
|
||||
/// 内部包掉 loading(show → cancel)、余额不足(code 8000)跳充值,
|
||||
/// 外部只透传成功/失败回调,并可带来源页面标识 [source]。
|
||||
class PayManager {
|
||||
PayManager._();
|
||||
|
||||
static final PayManager _instance = PayManager._();
|
||||
|
||||
factory PayManager() => _instance;
|
||||
|
||||
/// 下单购买(普通商品:视频/漫画/帖子/群聊/短剧单集等)
|
||||
/// [productID] 商品ID [productType] 商品类型,见 [ProductType]
|
||||
/// [source] 来源页面标识
|
||||
/// [contentID] 子内容ID(短剧买单集时传这一集的 id)——短剧归因就靠它和 productID(剧 id),不另发字段
|
||||
/// [checkoutContextId] 付费墙下发的结算上下文
|
||||
/// [requestId] 幂等键,一笔业务拆成多次请求时复用同一个 id;不传则自动生成,见 BuyService._idempotent
|
||||
/// [jsonTransformation] 要拿模型而不是原始 json 时传,结果在 onSuccess 的 data.data 里
|
||||
/// [jumpWalletOnInsufficient] 余额不足(code 8000)是否自动跳充值页,默认 true;
|
||||
/// 已在充值页、或要就地弹金币支付的场景传 false(仅跳过跳转;失败提示由网络层统一弹)
|
||||
/// [onSuccess] 支付成功回调,各页面自行处理副作用(Get.back / 刷新钱包等)
|
||||
/// [onFailure] 业务失败回调,可选(失败提示已由网络层统一弹)
|
||||
Future<void> buy(
|
||||
String? productID,
|
||||
ProductType productType, {
|
||||
String? source,
|
||||
String? couponId,
|
||||
int? goldVideoCouponNum,
|
||||
String? serviceId,
|
||||
String? contentID,
|
||||
String? checkoutContextId,
|
||||
String? requestId,
|
||||
Function(Map<String, dynamic> json)? jsonTransformation,
|
||||
bool jumpWalletOnInsufficient = true,
|
||||
required void Function(BaseRespBean data) onSuccess,
|
||||
void Function(BaseRespBean? data)? onFailure,
|
||||
}) {
|
||||
return _request(
|
||||
() => BuyService.buyVideo(
|
||||
productID,
|
||||
productType.value,
|
||||
source: source,
|
||||
couponId: couponId,
|
||||
goldVideoCouponNum: goldVideoCouponNum,
|
||||
serviceId: serviceId,
|
||||
contentID: contentID,
|
||||
checkoutContextId: checkoutContextId,
|
||||
requestId: requestId,
|
||||
jsonTransformation: jsonTransformation,
|
||||
),
|
||||
jumpWalletOnInsufficient: jumpWalletOnInsufficient,
|
||||
//一次性商品「已经买过了」等价于买到手,见 _request
|
||||
repeatAsSuccess: true,
|
||||
onSuccess: onSuccess,
|
||||
onFailure: onFailure,
|
||||
);
|
||||
}
|
||||
|
||||
/// 金币购买 VIP(含预售尾款)
|
||||
/// [productType] VIP 卡类型由服务端随卡下发(新人卡 5、预售卡 21 等),不是 [ProductType],保持原样透传
|
||||
/// [finalPayStatus] 预售业务:true 付尾款 / false 付预定款;null 非预售
|
||||
/// [requestId] 幂等键,同 [buy]
|
||||
/// [experimentId]/[experimentVariant]/[sessionId] VIP 卡皮 A/B:与 /mine/topay 对齐回传
|
||||
/// [mediaId]/[contentId]/[checkoutContextId] 短剧付费墙开卡归因,同 [buy]
|
||||
/// 其余同 [buy]
|
||||
Future<void> buyVip(
|
||||
int? productType,
|
||||
String? productID,
|
||||
String? productName,
|
||||
int? discountedPrice, {
|
||||
String? source,
|
||||
String? couponId = "",
|
||||
bool? finalPayStatus,
|
||||
String? experimentId,
|
||||
String? experimentVariant,
|
||||
String? sessionId,
|
||||
String? mediaId,
|
||||
String? contentId,
|
||||
String? checkoutContextId,
|
||||
String? requestId,
|
||||
bool jumpWalletOnInsufficient = true,
|
||||
required void Function(BaseRespBean data) onSuccess,
|
||||
void Function(BaseRespBean? data)? onFailure,
|
||||
}) {
|
||||
return _request(
|
||||
() => BuyService.buyVip(
|
||||
productType,
|
||||
productID,
|
||||
productName,
|
||||
discountedPrice,
|
||||
source: source,
|
||||
couponId: couponId,
|
||||
finalPayStatus: finalPayStatus,
|
||||
experimentId: experimentId,
|
||||
experimentVariant: experimentVariant,
|
||||
sessionId: sessionId,
|
||||
mediaId: mediaId,
|
||||
contentId: contentId,
|
||||
checkoutContextId: checkoutContextId,
|
||||
requestId: requestId,
|
||||
),
|
||||
jumpWalletOnInsufficient: jumpWalletOnInsufficient,
|
||||
onSuccess: onSuccess,
|
||||
onFailure: onFailure,
|
||||
);
|
||||
}
|
||||
|
||||
/// 统一下单流程:loading + 余额不足(code 8000)跳充值页。
|
||||
/// 有响应的失败提示由 HttpResponseInterceptor 全局弹;网络无响应(超时/断网)不额外处理。
|
||||
/// [repeatAsSuccess] 8005 重复购买是否当成功。
|
||||
/// 只有一次性商品(视频/漫画/帖子/群聊/短剧单集)能开——「已经买过了」就是已拥有;
|
||||
/// VIP 不能开:会员是可叠加续费的,8005 不代表用户拿到了东西,
|
||||
/// 当成功会误报购买埋点、弹「购买成功」并把充值页关掉
|
||||
Future<void> _request(
|
||||
Future<BaseRespBean> Function() request, {
|
||||
bool jumpWalletOnInsufficient = true,
|
||||
bool repeatAsSuccess = false,
|
||||
required void Function(BaseRespBean data) onSuccess,
|
||||
void Function(BaseRespBean? data)? onFailure,
|
||||
}) async {
|
||||
LoadingAlertWidget.show();
|
||||
final BaseRespBean data;
|
||||
try {
|
||||
data = await request();
|
||||
} finally {
|
||||
// 先关 loading 再回调,避免 onSuccess 里的 Get.back 误弹到 loading 弹窗。
|
||||
// 走 finally 是因为请求前的签名(generateRequestOption→_sign)会抛,
|
||||
// 抛出去就没人关这层全屏 loading 了,用户只能杀进程
|
||||
LoadingAlertWidget.cancel();
|
||||
}
|
||||
|
||||
// 注意 8005 走进来时 data.data 是错误体不是订单结果,onSuccess 里取字段要判类型
|
||||
if (data.isSuccess || (repeatAsSuccess && data.code == Code.REPEAT_BUY)) {
|
||||
onSuccess(data);
|
||||
return;
|
||||
}
|
||||
// 余额不足:拦截器只弹提示不跳转,这里补跳充值页(已在充值页则传 false 跳过)
|
||||
if (data.code == Code.NOT_ENOUGH_MONEY && jumpWalletOnInsufficient) {
|
||||
pushToWalletPage(tabPosition: 1);
|
||||
}
|
||||
onFailure?.call(data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user