راهنمای پرداخت درونبرنامهای مایکت در Flutter
برای مشاهده نمونه کامل پیادهسازی پرداخت درونبرنامهای مایکت در فلاتر، به پروژه نمونه رسمی در GitHub مراجعه کنید.
پلاگین myket_iap ابزار رسمی برای پیادهسازی پرداخت درونبرنامهای (In-App Billing) مایکت در برنامههای فلاتر است. این افزونه امکان راهاندازی سرویس پرداخت، استعلام محصولات و موجودی، شروع فرآیند خرید و مدیریت محصولات مصرفی و غیرمصرفی را فراهم میکند.
نکته: این پلاگین فقط از سیستمعامل اندروید پشتیبانی میکند.
قابلیتها
- راهاندازی با حداقل پیکربندی: پیکربندی ساده در پروژه اندروید فلاتر.
- پشتیبانی از انواع محصولات: مدیریت خریدهای یکباره شامل محصولات مصرفشدنی (مانند سکه یا اعتبار) و محصولات غیرمصرفی (مانند ارتقا به نسخه ویژه یا حذف تبلیغات).
- استعلام موجودی و قیمتگذاری: دریافت فهرست خریدهای فعال کاربر و مشخصات محصول (قیمت ریالی، عنوان و توضیحات) از مایکت.
- پشتیبانی از چند فروشگاه (Multi-Store): تغییر فروشگاه در زمان بیلد با استفاده از Gradle Product Flavors و مشخصههای مانیفست بدون تغییر کد Dart.
- مدیریت خطای استاندارد: ارائه کدهای نتیجه استاندارد به همراه پیامهای توصیفی خطا.
- سازگاری با نسخه های جاری: سازگار با Flutter 3+، Android Gradle Plugin 8+، Kotlin 2+ و Android SDK 34+.
پیشنیازها
۱. حساب کاربری در پنل توسعهدهندگان مایکت.
۲. ثبت برنامه در پنل مایکت و رزرو شناسه بسته (Package Name).
۳. دریافت کلید عمومی RSA پرداخت برنامه از پنل توسعهدهندگان مایکت.
۴. تعریف شناسههای کالا (SKU) در بخش پرداخت پنل برنامه.
۵. نصب برنامه مایکت روی دستگاه یا شبیهساز تست.
مراحل نصب
وابستگی myket_iap را به پروژه فلاتر اضافه کنید:
flutter pub add myket_iap
یا آن را مستقیماً د ر فایل pubspec.yaml قرار دهید:
dependencies:
flutter:
sdk: flutter
myket_iap: ^2.0.1
سپس دستور زیر را اجرا کنید:
flutter pub get
پیکربندی بخش اندروید (Android Setup)
۱. افزودن مخزن Maven رسمی مایکت
کتابخانه پرداخت بومی مایکت در مخزن اختصاصی مایکت میزبانی میشود. آدرس https://maven.myket.ir را به مخازن اندروید اضافه کنید:
در پروژههای مدرن فلاتر (android/settings.gradle.kts):
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven("https://maven.myket.ir")
}
}
در ساختار Groovy DSL (android/build.gradle یا settings.gradle):
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://maven.myket.ir' }
}
}
۲. تنظیم مشخصههای جایگزین در مانیفست (Manifest Placeholders)
مشخصههای سرویس مایکت را به defaultConfig در فایل android/app/build.gradle.kts یا android/app/build.gradle اضافه کنید:
در فایل android/app/build.gradle.kts (Kotlin DSL):
android {
...
defaultConfig {
...
val marketApplicationId = "ir.mservices.market"
val marketBindAddress = "ir.mservices.market.InAppBillingService.BIND"
manifestPlaceholders.putAll(
mapOf(
"marketApplicationId" to marketApplicationId,
"marketBindAddress" to marketBindAddress,
"marketPermission" to "$marketApplicationId.BILLING"
)
)
}
}
در فایل android/app/build.gradle (Groovy DSL):
android {
...
defaultConfig {
...
def marketApplicationId = "ir.mservices.market"
def marketBindAddress = "ir.mservices.market.InAppBillingService.BIND"
manifestPlaceholders += [
marketApplicationId: "${marketApplicationId}",
marketBindAddress : "${marketBindAddress}",
marketPermission : "${marketApplicationId}.BILLING"
]
}
}
۳. پشتیبانی از چند فروشگاه (Multi-Store & Product Flavors)
در صورت انتشار برنامه در چندین فروشگاه اندرویدی، میتوانید در android/app/build.gradle یا build.gradle.kts از Product Flavors استفاده کنید:
android {
...
flavorDimensions "store"
productFlavors {
myket {
dimension "store"
def marketApplicationId = "ir.mservices.market"
def marketBindAddress = "ir.mservices.market.InAppBillingService.BIND"
manifestPlaceholders = [
marketApplicationId: "${marketApplicationId}",
marketBindAddress : "${marketBindAddress}",
marketPermission : "${marketApplicationId}.BILLING"
]
}
bazaar {
dimension "store"
def marketApplicationId = "com.farsitel.bazaar"
def marketBindAddress = "ir.cafebazaar.pardakht.InAppBillingService.BIND"
manifestPlaceholders = [
marketApplicationId: "${marketApplicationId}",
marketBindAddress : "${marketBindAddress}",
marketPermission : "${marketApplicationId}.permission.PAY_THROUGH_BAZAAR"
]
}
}
}
۴. راهنمای مهاجرت از نسخههای ۱.۰.x به ۱.۱.۰ و بالاتر
اگر در حال بهروزرسانی از نسخه 1.0.6 یا قبلتر هستید:
۱. حذف مجوز دستی از فایل android/app/src/main/AndroidManifest.xml:
<!-- این خط را حذف کنید -->
<uses-permission android:name="ir.mservices.market.BILLING" />
۲. حذف بلوک <queries> از android/app/src/main/AndroidManifest.xml:
<!-- این بلوک را حذف کنید -->
<queries>
<package android:name="ir.mservices.market" />
<intent>
<action android:name="ir.mservices.market.InAppBillingService.BIND" />
<data android:mimeType="*/*" />
</intent>
</queries>
۳. افزودن مشخصههای جایگزین مانیفست (manifestPlaceholders) در گریدل مطابق بخش قبل. در نسخههای جدید، پلاگین مجوزها و اعلانهای دسترسی بسته را بهصورت خودکار ادغام میکند.
راهنمای گامبهگام پیادهسازی
تمام متدهای کلاس MyketIAP بهصورت static بوده و یک Future برمیگردانند.
گام اول: ایمپورت پکیج و مدلها
import 'package:myket_iap/myket_iap.dart';
import 'package:myket_iap/util/iab_result.dart';
import 'package:myket_iap/util/purchase.dart';
import 'package:myket_iap/util/inventory.dart';
import 'package:myket_iap/util/sku_details.dart';
import 'package:myket_iap/util/constants.dart';
گام دوم: راهاندازی و اتصال سرویس (init)
سرویس پرداخت را در ابتدای چرخه حیات برنامه (مثلاً در initState ویجت اصلی یا سرویس مدیریت پرداخت) مقداردهی کنید:
void initState() {
super.initState();
_initializeBilling();
}
Future<void> _initializeBilling() async {
const String myketRsaKey = "YOUR_RSA_KEY_FROM_MYKET_PANEL";
IabResult? result = await MyketIAP.init(
rsaKey: myketRsaKey,
enableDebugLogging: true, // در محیط Production مقدار false قرار دهید
);
if (result != null && result.isSuccess()) {
print("Billing setup successful: ${result.mMessage}");
// دریافت موجودی پس از اتصال موفق
_checkInventory();
} else {
print("Billing setup failed: ${result?.mMessage}");
}
}
پیش از اطمینان از موفقیت اتصال init (result.isSuccess() == true)، از فراخوانی متدهای خرید یا استعلام موجودی خودداری کنید.
گام سوم: استعلام موجودی و مشخصات محصولات (queryInventory)
برای استعلام خریدهای فعال کاربر و همچنین دریافت اطلاعات متا (عنوان، توضیحات و قیمت ریالی محصولات)، از متد queryInventory استفاده کنید:
Future<void> _checkInventory() async {
final Map<dynamic, dynamic> response = await MyketIAP.queryInventory(
querySkuDetails: true,
skus: ["gas", "premium_upgrade"],
);
IabResult? result = response[MyketIAP.RESULT];
Inventory? inventory = response[MyketIAP.INVENTORY];
if (result != null && result.isSuccess() && inventory != null) {
// ۱. بررسی مشخصات و قیمت محصولات
SkuDetails? gasDetails = inventory.mSkuMap["gas"];
if (gasDetails != null) {
print("Gas Price: ${gasDetails.mPrice}");
print("Gas Title: ${gasDetails.mTitle}");
}
// ۲. بررسی مالکیت محصولات غیرمصرفی
Purchase? premiumPurchase = inventory.mPurchaseMap["premium_upgrade"];
if (premiumPurchase != null) {
print("User owns premium upgrade: ${premiumPurchase.mOrderId}");
// فعالساز ی دسترسی ویژه
}
// ۳. بازیابی و مصرف خریدهای مصرفی پردازشنشده (در صورت کرش یا قطعی قبل از مصرف)
Purchase? gasPurchase = inventory.mPurchaseMap["gas"];
if (gasPurchase != null) {
await _consumeProduct(gasPurchase);
}
} else {
print("Failed to query inventory: ${result?.mMessage}");
}
}
گام چهارم: شروع فرآیند خرید (launchPurchaseFlow)
برای شروع فرآیند خرید یک محصول و نمایش درگاه پرداخت مایکت:
Future<void> _buyProduct(String sku) async {
// رشته اختیاری برای اعتبارسنجی سمت سرور و جلوگیری از تقلب
const String developerPayload = "custom_payload_or_order_token";
final Map<dynamic, dynamic> response = await MyketIAP.launchPurchaseFlow(
sku: sku,
payload: developerPayload,
);
IabResult? result = response[MyketIAP.RESULT];
Purchase? purchase = response[MyketIAP.PURCHASE];
if (result != null && result.isSuccess() && purchase != null) {
print("Purchase successful for SKU: ${purchase.mSku}");
print("Order ID: ${purchase.mOrderId}");
print("Purchase Token: ${purchase.mToken}");
// در صورت مصرفی بودن، بلافاصله مصرف شود
if (purchase.mSku == "gas") {
await _consumeProduct(purchase);
} else {
// محصول غیرمصرفی (ارتقای دائمی)
_unlockFeature();
}
} else {
print("Purchase cancelled or failed: ${result?.mMessage}");
}
}