新增:三方支付完整调用
This commit is contained in:
parent
ea14c36d63
commit
066d8b7391
@ -88,6 +88,21 @@ abstract final class PaymentApi {
|
||||
);
|
||||
}
|
||||
|
||||
/// 获取订单详情(用于三方支付 webview 关闭后轮询)
|
||||
static Future<ApiResponse> getOrderDetail({
|
||||
required String asset,
|
||||
required String federation,
|
||||
}) async {
|
||||
return _client.request(
|
||||
path: '/v1/payment/getOrderDetail',
|
||||
method: 'GET',
|
||||
queryParams: {
|
||||
'asset': asset,
|
||||
'federation': federation,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Google 支付结果回调。body 为 sample(signature)、merchant(purchaseData)、federation(id)、asset(userId),见 docs/googlepay.md。
|
||||
static Future<ApiResponse> googlepay({
|
||||
required String sample,
|
||||
|
||||
@ -80,7 +80,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
icon: LucideIcons.chevron_right,
|
||||
onTap: () => Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => PaymentWebViewScreen(
|
||||
builder: (_) => const PaymentWebViewScreen(
|
||||
paymentUrl: 'http://www.petsheroai.xyz/privacy.html',
|
||||
title: 'Privacy Policy',
|
||||
),
|
||||
@ -92,7 +92,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
icon: LucideIcons.chevron_right,
|
||||
onTap: () => Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => PaymentWebViewScreen(
|
||||
builder: (_) => const PaymentWebViewScreen(
|
||||
paymentUrl: 'http://www.petsheroai.xyz/terms.html',
|
||||
title: 'User Agreement',
|
||||
),
|
||||
@ -143,18 +143,18 @@ class _ProfileHeader extends StatelessWidget {
|
||||
? CachedNetworkImage(
|
||||
imageUrl: avatarUrl!,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (_, __) => Icon(
|
||||
placeholder: (_, __) => const Icon(
|
||||
LucideIcons.user,
|
||||
size: 40,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
errorWidget: (_, __, ___) => Icon(
|
||||
errorWidget: (_, __, ___) => const Icon(
|
||||
LucideIcons.user,
|
||||
size: 40,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
: const Icon(
|
||||
LucideIcons.user,
|
||||
size: 40,
|
||||
color: AppColors.textSecondary,
|
||||
@ -162,7 +162,7 @@ class _ProfileHeader extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
Text(
|
||||
userName ?? 'Guest',
|
||||
userName ?? 'VIP',
|
||||
style: AppTypography.bodyLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
@ -210,11 +210,11 @@ class _BalanceCard extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.shadowLight,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -314,11 +314,11 @@ class _MenuItem extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surface,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.shadowLight,
|
||||
blurRadius: 6,
|
||||
offset: const Offset(0, 2),
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -275,6 +275,9 @@ class _RechargeScreenState extends State<RechargeScreen>
|
||||
builder: (_) => PaymentWebViewScreen(paymentUrl: payUrl),
|
||||
),
|
||||
);
|
||||
if (mounted && orderId != null && orderId.isNotEmpty) {
|
||||
_startOrderPolling(orderId: orderId, userId: userId);
|
||||
}
|
||||
_showSnackBar(
|
||||
context, 'Order created. Complete payment in the page.');
|
||||
AdjustEvents.trackPurchaseSuccess();
|
||||
@ -299,6 +302,42 @@ class _RechargeScreenState extends State<RechargeScreen>
|
||||
}
|
||||
}
|
||||
|
||||
/// 三方支付 webview 关闭后轮询订单详情,间隔 1/3/7/15/31/63 秒;status 为 SUCCESS|FAILED|CANCELED 或订单不存在或轮询结束则停止;SUCCESS 时刷新用户信息
|
||||
void _startOrderPolling({required String orderId, required String userId}) {
|
||||
const delays = [1, 2, 4, 8, 16, 32]; // 累计 1,3,7,15,31,63 秒
|
||||
Future<void> poll(int index) async {
|
||||
if (index >= delays.length) return;
|
||||
await Future<void>.delayed(Duration(seconds: delays[index]));
|
||||
if (!mounted) return;
|
||||
try {
|
||||
final res = await PaymentApi.getOrderDetail(
|
||||
asset: userId,
|
||||
federation: orderId,
|
||||
);
|
||||
if (!mounted) return;
|
||||
if (!res.isSuccess || res.data == null) {
|
||||
RechargeScreen._log.d('订单轮询 orderId=$orderId 订单不存在或请求失败');
|
||||
return;
|
||||
}
|
||||
final data = res.data as Map<String, dynamic>?;
|
||||
final status = data?['line']?.toString().toUpperCase();
|
||||
RechargeScreen._log.d('订单轮询 orderId=$orderId status=$status');
|
||||
if (status == 'SUCCESS' || status == 'FAILED' || status == 'CANCELED') {
|
||||
if (status == 'SUCCESS') {
|
||||
refreshAccount();
|
||||
}
|
||||
return;
|
||||
}
|
||||
poll(index + 1);
|
||||
} catch (e) {
|
||||
RechargeScreen._log.w('订单轮询异常 orderId=$orderId: $e');
|
||||
poll(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
poll(0);
|
||||
}
|
||||
|
||||
/// ceremony==GooglePay 或 resource==GOOGLEPAY 时走谷歌内购并上报
|
||||
static bool _isGooglePay(String paymentMethod, String? subPaymentMethod) {
|
||||
final r = paymentMethod.trim().toLowerCase();
|
||||
@ -534,8 +573,8 @@ class _RechargeScreenState extends State<RechargeScreen>
|
||||
else
|
||||
...List.generate(_activities.length, (i) {
|
||||
final item = _activities[i];
|
||||
final isRecommended = false;
|
||||
final isPopular = false;
|
||||
const isRecommended = false;
|
||||
const isPopular = false;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: i < _activities.length - 1 ? AppSpacing.xl : 0,
|
||||
@ -760,13 +799,13 @@ class _PaymentIcon extends StatelessWidget {
|
||||
? Image.network(
|
||||
iconUrl!,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (_, __, ___) => Icon(
|
||||
errorBuilder: (_, __, ___) => const Icon(
|
||||
LucideIcons.credit_card,
|
||||
size: 24,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
: const Icon(
|
||||
LucideIcons.credit_card,
|
||||
size: 24,
|
||||
color: AppColors.primary,
|
||||
@ -825,11 +864,11 @@ class _TierCardFromActivity extends StatelessWidget {
|
||||
color: AppColors.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: AppColors.border),
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.shadowLight,
|
||||
blurRadius: 6,
|
||||
offset: const Offset(0, 2),
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -962,7 +1001,7 @@ class _CreditsSection extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(LucideIcons.sparkles, size: 28, color: AppColors.surface),
|
||||
const Icon(LucideIcons.sparkles, size: 28, color: AppColors.surface),
|
||||
const SizedBox(width: AppSpacing.md),
|
||||
Text(
|
||||
currentCredits,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user