feat: add payment

This commit is contained in:
2026-07-05 16:03:15 +03:30
parent c2356b3242
commit 5017e9a613
30 changed files with 470 additions and 65 deletions
@@ -0,0 +1,46 @@
import 'package:flutter_poolakey/flutter_poolakey.dart';
import '../config.dart';
import 'payment_gateway.dart';
/// پرداختِ درون‌برنامه‌ایِ کافه‌بازار (Poolakey) — flavorِ `bazaar`.
class BazaarPaymentGateway implements PaymentGateway {
bool _connected = false;
@override
String get store => 'bazaar';
@override
Future<void> connect() async {
if (_connected) return;
await FlutterPoolakey.connect(
AppConfig.bazaarRsaKey,
onDisconnected: () => _connected = false,
);
_connected = true;
}
@override
Future<PurchaseResult> purchase(String productSku) async {
try {
await connect();
final info = await FlutterPoolakey.purchase(
productSku,
payload: 'hakemsho',
);
if (info.purchaseToken.isEmpty) {
return const PurchaseResult.failure();
}
return PurchaseResult.success(
token: info.purchaseToken,
purchaseData: info.originalJson,
signature: info.dataSignature,
// کالای مصرفی باید مصرف شود تا خریدِ مجدد ممکن باشد.
onConfirmed: () => FlutterPoolakey.consume(info.purchaseToken),
);
} catch (_) {
// کنسل‌شدن یا خطای بازار ⇒ شکستِ خرید.
return const PurchaseResult.failure();
}
}
}
+22
View File
@@ -0,0 +1,22 @@
import 'payment_gateway.dart';
/// درگاهِ توسعه: بدونِ اتصال به فروشگاه، یک توکنِ ساختگی می‌سازد. برای اجرای
/// معمولیِ `flutter run` (بدونِ flavor) استفاده می‌شود؛ بک‌اند در حالتِ
/// DevFallback آن را می‌پذیرد.
class DevPaymentGateway implements PaymentGateway {
@override
String get store => 'dev';
@override
Future<void> connect() async {}
@override
Future<PurchaseResult> purchase(String productSku) async {
final stamp = DateTime.now().millisecondsSinceEpoch;
return PurchaseResult.success(
token: 'dev-$productSku-$stamp',
purchaseData: '{"productId":"$productSku","purchaseToken":"dev-$stamp"}',
signature: 'dev',
);
}
}
@@ -0,0 +1,40 @@
import 'package:myket_iap/myket_iap.dart';
import 'package:myket_iap/util/purchase.dart';
import '../config.dart';
import 'payment_gateway.dart';
/// پرداختِ درون‌برنامه‌ایِ مایکت (flavorِ `myket`).
class MyketPaymentGateway implements PaymentGateway {
bool _inited = false;
@override
String get store => 'myket';
@override
Future<void> connect() async {
if (_inited) return;
await MyketIAP.init(rsaKey: AppConfig.myketRsaKey, enableDebugLogging: false);
_inited = true;
}
@override
Future<PurchaseResult> purchase(String productSku) async {
await connect();
final Map<dynamic, dynamic> result =
await MyketIAP.launchPurchaseFlow(sku: productSku, payload: 'hakemsho');
final Purchase? purchase = result[MyketIAP.PURCHASE] as Purchase?;
if (purchase == null || purchase.mToken.isEmpty) {
return const PurchaseResult.failure();
}
return PurchaseResult.success(
token: purchase.mToken,
purchaseData: purchase.mOriginalJson,
signature: purchase.mSignature,
// کالای مصرفی (سکه/بلیت) باید مصرف شود تا کاربر بتواند دوباره بخرد.
onConfirmed: () => MyketIAP.consume(purchase: purchase),
);
}
}
+40
View File
@@ -0,0 +1,40 @@
/// نتیجه‌ی یک خریدِ فروشگاهی، مستقل از اینکه کدام SDK آن را تولید کرده.
///
/// بک‌اند برای تأییدِ آفلاینِ RSA به [purchaseData] (JSONِ خام) و [signature]
/// نیاز دارد؛ [token] هم برای تأییدِ سمت‌سرور/سازگاری فرستاده می‌شود.
class PurchaseResult {
final bool success;
final String token;
final String purchaseData;
final String signature;
/// پاک‌سازیِ لازم پس از تأییدِ بک‌اند (مثلاً مایکت باید خرید را consume کند).
final Future<void> Function()? onConfirmed;
const PurchaseResult.success({
required this.token,
required this.purchaseData,
required this.signature,
this.onConfirmed,
}) : success = true;
const PurchaseResult.failure()
: success = false,
token = '',
purchaseData = '',
signature = '',
onConfirmed = null;
}
/// انتزاعِ SDKِ پرداختِ درون‌برنامه‌ایِ مخصوصِ هر فروشگاه. هر flavor یک پیاده‌سازی
/// ثبت می‌کند؛ بقیه‌ی اپ از SDK بی‌خبر می‌ماند.
abstract class PaymentGateway {
/// شناسه‌ی فروشگاه که به بک‌اند فرستاده می‌شود (`myket` | `bazaar`).
String get store;
/// جریانِ خریدِ [productSku] (همان UUIDِ محصول در پنلِ فروشگاه) را باز می‌کند.
Future<PurchaseResult> purchase(String productSku);
/// اتصالِ اولیه (در صورتِ نیاز). پیش از اولین خرید صدا زده می‌شود.
Future<void> connect() async {}
}