خرید با Package و پیگیری نتیجه

خرید فروشگاهی و Direct با پکیج، Activity کاتلین، idempotencyKey، نتیجه نامشخص و رویداد و تاریخچه نسخه 0.2.1.

ابتدا آفرینگ مناسب را دریافت و همان Package را نگه دارید. SDK از آن productIdentifier، offeringIdentifier، packageIdentifier و نوع محصول را می‌خواند؛ دولوپر نباید این اطلاعات را از متن نمایشی حدس بزند.

Flutter

import 'package:inappify_sdk/inappify_sdk.dart';

Future<Map<String, Object?>> purchaseSelected(
  PackageModel selected, String savedAttemptId,
) async {
  final result = await InappifySdk.purchaseResult(InappifyPurchaseParams(
    packageToPurchase: selected,
    idempotencyKey: savedAttemptId,
  ));
  if (!result.isSuccess) return {}; // Keep uncertain attempt/context for recovery.
  final purchase = result.data;
  // Route to access refresh, durable delivery, or validated browser navigation.
  return {
    'workflow': purchase?.mobile?.storePurchaseStatus,
    'deliveryId': purchase?.mobile?.deliveryId,
    'paymentUrl': purchase?.url,
  };
}

packageToPurchase اجباری است. idempotencyKey، paywallId و paywallRevision اختیاری‌اند و پیش‌فرض null دارند؛ attribution آیدی/نسخه باید جفت و مربوط به همان paywall باشد. در نبود key، SDK یکی می‌سازد. برای پیگیری میزبان پس از بسته‌شدن اپ، پیش از خرید یک key معتبر ذخیره و فقط برای همان تلاش منطقی استفاده کنید. کلید ۱ تا ۱۲۸ کاراکتر از حروف، رقم و _ . : - دارد.

پل Android خودش Activity را فراهم می‌کند. API عمومی جدید ورودی apiKey، market/RSA، مبلغ تخفیف، isCrypto یا override نوع محصول ندارد. نسخه ساده purchase(params) nullable است؛ برای تصمیم مالی از Result استفاده کنید.

Kotlin: فروشگاه و Direct

import android.app.Activity
import com.inappify.sdk.*

suspend fun purchaseStore(
    client: InappifyV2Client,
    activity: Activity,
    selected: InappifyPackage,
    savedAttemptId: String,
) = client.purchase(
    InappifyPurchaseParams.Builder(activity, selected)
        .idempotencyKey(savedAttemptId)
        .build()
)

suspend fun purchaseDirect(
    client: InappifyV2Client,
    selected: InappifyPackage,
    savedAttemptId: String,
) = client.purchase(
    InappifyPurchaseParams.Builder(selected)
        .idempotencyKey(savedAttemptId)
        .build()
)

Activity برای شروع خرید تازه استور لازم است؛ Builder بدون Activity برای Direct یا ادامه کار پرداخت‌شده کاربرد دارد. Activity-backed params را بیرون عمر Activity نگه ندارید. سازنده اختیاری paywallAttribution(id, revision) مخصوص context معتبر Direct است؛ مبلغ خرید را تعیین نمی‌کند.

نتیجه عملیات و نتیجه پرداخت

وضعیتکار میزبان
NEEDTOPAY و URLادامه در مرورگر معتبر؛ بدون اعطای دسترسی
PROCESSING یا خطای retryable نامشخصانتظار/بازیابی همان تلاش؛ بدون پرداخت دوم
COMPLETED، RESTORED، ALREADY_PROCESSEDبررسی CustomerInfo برای دسترسی
DELIVERY_REQUIREDتحویل ماندگار و یکتای مصرفی
CONSUME_REQUIREDتکمیل مرحله مصرف؛ بدون افزودن دوباره موجودی
REJECTEDعدم اعطای دسترسی یا موجودی

همه این برچسب‌ها لزوماً به‌صورت Success برنمی‌گردند؛ بعضی وضعیت‌های درانتظار در Failure قابل‌بازیابی ظاهر می‌شوند. در Flutter از mobile.storePurchaseStatus و در Kotlin از storePurchaseStatus استفاده کنید. isSuccess فقط موفقیت عملیات SDK است.

پرداخت مستقیم

میزبان لینک را باز می‌کند. روی موبایل ابتدا InappifySdk.isPaymentUrlAllowed(url) را await و success/data برابر true را بررسی کنید؛ Kotlin extension client.isPaymentUrlAllowed(url) Boolean است. مقصد Direct مجاز طبق تنظیم SDK بررسی می‌شود؛ query یا URL دلخواه کاربر را جایگزین نکنید.

Android نتیجه Direct جدید را با attempt اصلی و backend سازگار پیگیری می‌کند. روی iOS، key این نسخه فقط هم‌بستگی محلی نتیجه/خطاست و داخل درخواست Direct ارسال نمی‌شود؛ تکرار با همان key تضمین deduplication سرور نیست. نتیجه مبهم را پیش از پرداخت بعدی بازیابی و بررسی کنید.

وب از checkout جداگانه استفاده می‌کند و return آن با checkout ID بررسی می‌شود؛ نمونه وب را دنبال کنید. لینک برگشت، بستن مرورگر و موفقیت ایجاد checkout اثبات پرداخت نیستند.

Android: پیگیری پس از بسته‌شدن اپ

import 'dart:async';
import 'package:inappify_sdk/inappify_sdk.dart';

StreamSubscription<InappifyPurchaseAttempt> observeAndroidPurchases(
  void Function(InappifyPurchaseAttempt) reconcileMatchingAttempt,
) => InappifySdk.purchaseUpdates.listen(reconcileMatchingAttempt);

Future<void> inspectSavedAttempt(String savedAttemptId) async {
  final result = await InappifySdk.getPurchaseAttempt(savedAttemptId);
  if (!result.isSuccess || result.data == null) return; // Retain unknown attempt.
  final attempt = result.data!;
  if (attempt.attemptId != savedAttemptId) return;
  // Also match the saved customer/app/product/offering/market context.
  // Inspect attempt.status, attempt.purchase and attempt.failure.
}
import com.inappify.sdk.*

suspend fun inspectSavedAttempt(client: InappifyV2Client, savedAttemptId: String) {
    when (val result = client.getPurchaseAttempt(savedAttemptId)) {
        is InappifyResult.Failure -> return
        is InappifyResult.Success -> {
            val attempt = result.data ?: return // Unknown: retain host journal.
            if (attempt.attemptId != savedAttemptId) return
            // Match original scope; inspect status, purchase and error.
        }
    }
}

پیش از خرید listener را وصل کنید؛ هنگام پایان مالک UI اشتراک را cancel کنید. Kotlin از client.addEventListener { event -> event.purchaseAttempt ... } استفاده می‌کند و registration قابل close می‌دهد. getPurchaseAttempts() نیز تاریخچه همان نشست را می‌خواند؛ شبکه و استور اجرا نمی‌کند.

وضعیت تلاش‌ها pending/succeeded/failed است. موفقیت lookup با null یعنی نامشخص، نه شکست. تاریخچه Android رمز‌شده، محدود و وابسته به app/customer/market است؛ events ممکن است تکرار شوند یا زمان بسته‌بودن اپ از دست بروند. پس از configure تاریخچه را برای تلاش ذخیره‌شده بررسی کنید. وجود موفقیت قبلی نیز جای CustomerInfo جاری یا موجودی ماندگار نیست.

خطای شبکه، تغییر حساب، خروجی خالی sync و رویداد نامرتبط نباید سابقه تلاش را پاک یا خرید جدید شروع کند. این API تاریخچه روی iOS و Flutter Web پشتیبانی نمی‌شود.

تحویل مصرفی · تخفیف · دسترسی و بازیابی