feat: add payment
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
}
|
||||
Reference in New Issue
Block a user