SDK وب و PWA

راهنمای @inappifylabs/web-sdk نسخه 0.2.0؛ نصب، ورود، آفرینگ، تخفیف، checkout، برگشت پرداخت و نصب پل Flutter Web.

Web SDK یک کتابخانه headless برای وب‌سایت و PWA است؛ UI پرداخت یا Paywall renderer، StoreKit و خرید بازار/مایکت را به مرورگر اضافه نمی‌کند. نسخه این صفحه 0.2.0 است و از نسخه Flutter مستقل است.

نصب و پیش‌نیاز

npm install --save-exact @inappifylabs/web-sdk@0.2.0

در پنل، Web App، origin مجاز، offerings و checkout را فعال کنید. خرید مهمان فقط در صورت فعال‌بودن آن مجاز است. ویژگی‌ها باید allowlist نوشتن داشته باشند. کلید Public Web App Key را بگیرید؛ کلید موبایل یا سرور را جای آن نگذارید. این SDK را در محیط مرورگر و پس از آماده‌شدن APIهای مرورگر شروع کنید؛ برای SSR یک نشست مشترک بین کاربران نسازید.

ایجاد یک client

import { InAppify } from '@inappifylabs/web-sdk';

export async function startWebSdk(publicWebKey: string) {
  return InAppify.configure({
    apiKey: publicWebKey,
    country: 'IR',
    appVersion: '2.0.0', // Host app version, not SDK version.
  });
}

یک client را در مالک نشست اپ نگه دارید. configure نشست را ایجاد/بازیابی، customer را بارگذاری و offerings را پیش‌واکشی می‌کند. پیش‌فرض‌ها: country برابر IR، appVersion خالی، timeout برابر ۱۵ ثانیه، TTL آفرینگ پنج دقیقه و prefetch فعال است. sdkVersion را به‌جای appVersion نفرستید؛ نسخه SDK داخل build وجود دارد.

توکن دسترسی در memory و refresh token در IndexedDB قابل‌استفاده نگه‌داری می‌شود؛ در حالت memory-only بازیابی پس از reload تضمین نمی‌شود. CustomerInfo کش پایدار جداگانه ندارد. storage و توکن‌ها را از طرف اپ بازنویسی یا در localStorage کپی نکنید.

هویت و پیشنهاد

برای ورود، client.logIn({identityTicket}) یا client.logIn({challengeId, code})؛ برای خروج client.logOut() را استفاده کنید. ticket باید تازه و از backend احرازشده خودتان باشد؛ نمونه‌های هویت شامل OTP است.

getCurrentOffering()، getCurrentOffering('upgrade') و getOffering('special_offer') سه حالت نمایش‌اند. getOfferings() مجموعه را می‌دهد؛ all آرایه است. از نام محصول پکیج جدید نسازید. آفرینگ‌ها نمونه کامل دارند.

خرید و return

import type { InAppify } from '@inappifylabs/web-sdk';

export async function createWebPayment(client: InAppify, savedAttemptId: string) {
  const offering = await client.getCurrentOffering('upgrade');
  const selected = offering?.packages[0];
  if (!selected)
    return null;
  return client.purchase({
    package: selected,
    idempotencyKey: savedAttemptId,
  }, { redirect: false });
}

export async function verifyWebReturn(client: InAppify, savedCheckoutId: string) {
  const result = await client.handlePaymentReturn(savedCheckoutId, {
    timeoutMs: 60000,
    intervalMs: 1500,
  });
  return result.checkout.status === 'Paid';
}

purchase({package}) به‌طور پیش‌فرض redirect می‌کند. در نمونه redirect: false داده checkout را به میزبان می‌دهد؛ پیش از بازکردن nextAction.url، checkout ID و تلاش/هویت را ماندگار کنید و مقصد را طبق سیاست origin میزبان اعتبارسنجی کنید. createCheckout(params) خودش navigate نمی‌کند.

getCheckoutStatus(id) یک query و handlePaymentReturn(id, options) polling محدود است. پیش‌فرض polling شصت ثانیه با فاصله ۱۵۰۰ میلی‌ثانیه است. فقط وضعیت Paid تأییدشده از سرور پرداخت را ثابت می‌کند؛ برای دسترسی CustomerInfo تازه و برای مصرفی تحویل ماندگار لازم است. query برگشت، بستن مرورگر و لغو polling، پرداخت را تأیید یا لغو نمی‌کنند. timeout تلاش را نامشخص نگه می‌دارد.

CustomerInfo و sync

حالت‌های زیر مستقل‌اند؛ در کد واقعی سیاست موردنیاز را انتخاب کنید، نه اجرای همه آن‌ها پشت‌سرهم:

import type { InAppify } from '@inappifylabs/web-sdk';
import { CacheFetchPolicy } from '@inappifylabs/web-sdk';

export async function customerReadAlternatives(client: InAppify) {
  await client.getCustomerInfo(); // FETCH_CURRENT by default.
  await client.getCustomerInfo(CacheFetchPolicy.FETCH_CURRENT);
  await client.getCustomerInfo(CacheFetchPolicy.CACHE_ONLY);
  await client.getCustomerInfo(CacheFetchPolicy.NOT_STALE_CACHED_OR_CURRENT);
  await client.getCustomerInfo(CacheFetchPolicy.CACHED_OR_FETCHED);
  return client.hasEntitlement('premium');
}

JS پیش‌فرض FETCH_CURRENT دارد. بقیه سیاست‌ها به‌ترتیب فقط کش، کش تازه یا سرور، و کش فوری همراه رفرش stale هستند. آستانه stale پنج دقیقه برای صفحه visible و ۲۵ ساعت برای hidden است. entitlements در JS آرایه‌اند؛ helperهای client از کپی‌کردن کد map فلاتر جلوگیری می‌کنند.

setAttributes({...}) مستقیم می‌نویسد. syncAttributesAndOfferingsIfNeeded() customer snapshot و سپس آفرینگ تازه را می‌گیرد؛ ۵ اجرای پذیرفته‌شده در ۶۰ ثانیه، احتساب شکست و عدم reset با تغییر هویت دارد. فراخوانی اضافه کش همان نشست یا RATE_LIMITED_NO_CACHE می‌دهد. صف آفلاین native در وب وجود ندارد.

تخفیف و خطا

validateDiscountCode(code) پیشنهاد جاری و validateDiscountCode(code, offeringId) پیشنهاد صریح را قیمت‌گذاری می‌کند. برای placement، شناسه آفرینگ انتخاب‌شده را بدهید؛ شناسه خالی/نامعتبر را به default تبدیل نکنید. نمونه خرید تخفیف‌دار از خروجی server-priced استفاده می‌کند.

Promiseهای ناموفق throw/reject می‌شوند؛ isInAppifyError(error) خطای ساختاریافته را تشخیص می‌دهد. exception یا signal abort به معنی عدم commit پرداخت نیست. checkEntitlement و گزینه‌های قدیمی forceRefresh برای سازگاری باقی‌اند؛ کد جدید از سیاست CustomerInfo و helper دسترسی استفاده کند.

Flutter Web: نصب پل پیش از bootstrap

وابستگی npm بالا باید توسط build میزبان bundle شود؛ Flutter bare npm import را به asset مرورگر تبدیل نمی‌کند. entry رسمی web/inappify_web_sdk_entry.mjs در مخزن Flutter را در bundler میزبان وارد کنید؛ مرز آن مشابه این است:

import { InAppify, isInAppifyError, SDK_VERSION } from '@inappifylabs/web-sdk';

Object.defineProperty(globalThis, '__inappifyFlutterWebSdk', {
  value: Object.freeze({ InAppify, version: SDK_VERSION, isInAppifyError }),
  writable: false,
  configurable: false,
});

خروجی bundle را با نام نمونه inappify-provider.js خودمیزبان بارگذاری و bootstrap تکراری Flutter را با loader ترتیبی زیر جایگزین کنید:

<script type="module">
  await import('./inappify-provider.js');
  const bootstrap = document.createElement('script');
  bootstrap.src = './flutter_bootstrap.js';
  document.body.appendChild(bootstrap);
</script>

این فایل باید خروجی bundle باشد، نه فایل خام با bare import. entry رسمی از نصب دوباره جلوگیری می‌کند؛ کد نمونه را برای overwrite مرز موجود اجرا نکنید. CSP میزبان باید asset و سرویس‌های لازم را مجاز کند. پس از آن، فقط facade فلاتر را استفاده کنید؛ یک client JS موازی نسازید.

برای شروع مهمان InappifySdk.configureResult(apiKey: 'YOUR_PUBLIC_WEB_APP_KEY') و برای حساب معتبر helper ورود وب را استفاده کنید.

Flutter Web: checkout و محدودیت‌ها

import 'package:inappify_sdk/inappify_sdk.dart';

Future<InappifySdkResult<InappifyCheckoutResult>> createDiscountedCheckout({
  required PackageModel selected,
  required String code,
  required String savedAttemptId,
}) {
  final product = selected.product?.identifier;
  final offering = selected.offeringIdentifier;
  final package = selected.identifier;
  if (product == null || offering == null || package == null) {
    throw ArgumentError('Use a complete SDK catalog Package.');
  }
  return InappifySdk.createCheckout(
    productIdentifier: product,
    offeringIdentifier: offering,
    packageIdentifier: package,
    discountCode: code,
    idempotencyKey: savedAttemptId,
  );
}

Future<bool> verifyFlutterWebReturn(String savedCheckoutId) async {
  final result = await InappifySdk.handlePaymentReturnResult(
    checkoutId: savedCheckoutId, timeoutMs: 60000, intervalMs: 1500,
  );
  return result.isSuccess && result.data?.isPaid == true;
}

شناسه‌ها باید از پکیج آفرینگ معتبر/تخفیف‌خورده بیایند. createCheckout برای discountCode صریح مناسب است، چون InappifyPurchaseParams فلاتر ورودی code ندارد. purchaseResult(Package) هم پشتیبانی می‌شود اما نتیجه را به PurchaseResult نگاشت و JS را با redirect خاموش اجرا می‌کند؛ navigation با میزبان است.

اگر checkoutId به handlePaymentReturnResult() داده نشود، facade از pending checkout ذخیره‌شده در sessionStorage استفاده می‌کند. getPendingCheckoutIdResult() و getCheckoutStatus(checkoutId:) نیز در دسترس‌اند. داده storage اثبات پرداخت نیست و خرابی storage نیز اثبات نبود checkout نیست.

در پل Flutter 0.2.1، combined sync و متدهای native delivery/history پیاده نشده‌اند و fetchPolicy به JS منتقل نمی‌شود. getOffering از helper مجموعه کار می‌کند. جدول دقیق قابلیت‌ها را مبنا قرار دهید؛ برای دورزدن محدودیت از client دوم یا کلاس‌های src استفاده نکنید.

مرجع Web 0.2.0 · مرجع متدها