Dart (Flutter)
flutter_orca gives you one API that works everywhere Flutter runs. On iOS/macOS/Android it drives native in-app purchases under the hood; on web/Windows/Linux it routes through Stripe or GoCardless checkout. You write the same code either way — the package figures out which path to take.
Install it
Section titled “Install it”flutter pub add orcaThe flow, end to end
Section titled “The flow, end to end”1. Create an Orca instance
Section titled “1. Create an Orca instance”Unlike the native SDKs, this one isn’t a global singleton — you create an instance and keep it around (a provider, a singleton service, whatever fits your app’s state management):
import 'package:orca/orca.dart';
final orca = Orca( publicKey: 'YOUR_ORCA_PUBLIC_KEY', environment: OrcaEnvironment.sandbox, // .production for release);2. Identify the customer
Section titled “2. Identify the customer”await orca.identify('user@example.com');This also opens a live event stream (see step 5) so your app hears about purchases made outside your current session — handy on desktop/web where a purchase can finish in another tab.
3. Show the paywall
Section titled “3. Show the paywall”final products = await orca.queryProducts(ExternalStore.stripe);
for (final product in products) { print('${product.name}: ${product.formattedPrice}');}On mobile and macOS, the ExternalStore argument is ignored — native IAP is used automatically. On web/Windows/Linux, it decides whether you’re pricing against Stripe or GoCardless.
4. Let them buy it
Section titled “4. Let them buy it”final entitlements = await orca.listEntitlements();final selected = entitlements.first;
await orca.purchase( selected, externalStore: ExternalStore.stripe, redirectUrl: 'https://yourapp.com/success', failureRedirectUrl: 'https://yourapp.com/failure',);redirectUrl / failureRedirectUrl only matter for the web/desktop checkout path — on mobile they’re ignored since the native purchase sheet doesn’t redirect anywhere.
5. React to the purchase as it happens
Section titled “5. React to the purchase as it happens”Purchases aren’t always synchronous — the App Store, Play Store, and web checkout all confirm asynchronously. Listen to the event stream instead of assuming purchase() resolving means success:
orca.purchaseEvents.listen((event) { switch (event.event) { case PurchaseEventType.success: // unlock the feature, refresh entitlements case PurchaseEventType.canceled: // user backed out, no action needed case PurchaseEventType.failed: // show an error, maybe suggest retry case PurchaseEventType.error: // something went wrong on our end }});6. Gate features behind what they own
Section titled “6. Gate features behind what they own”final active = await orca.getActiveEntitlements();final isPro = active.any((e) => e.entitlementId == 'pro_tier');Or, if you want product-shaped data (price, name) instead of raw entitlement records:
final activeProducts = await orca.activeProduct();7. Upgrading or downgrading a plan
Section titled “7. Upgrading or downgrading a plan”Play Store and App Store handle proration automatically once you set up an Offering in the dashboard. For Stripe/GoCardless, tell Orca explicitly what the customer is switching from:
await orca.purchase( newEntitlement, externalStore: ExternalStore.stripe, redirectUrl: 'https://yourapp.com/success', failureRedirectUrl: 'https://yourapp.com/failure', proratedProduct: currentEntitlement.products.stripe, prorationMode: ProrationMode.upgrade, // or .downgrade);8. Let them manage their subscription
Section titled “8. Let them manage their subscription”For App Store/Play Store subscriptions, customers manage or cancel through the platform’s own settings, not your app. Link them there:
// AndroidlaunchUrl(Uri.parse(orca.playStoreManagementUrl));
// iOS / macOSlaunchUrl(Uri.parse(orca.appStoreManagementUrl));9. Sign out and clean up
Section titled “9. Sign out and clean up”orca.logout(); // clears identity, stops the event stream
// when the widget/service holding this Orca instance is disposed:orca.dispose();A complete mini example
Section titled “A complete mini example”class PaywallScreen extends StatefulWidget { @override State<PaywallScreen> createState() => _PaywallScreenState();}
class _PaywallScreenState extends State<PaywallScreen> { List<OrcaEntitlement> _entitlements = []; StreamSubscription? _sub;
@override void initState() { super.initState(); _sub = orca.purchaseEvents.listen((event) { if (event.event == PurchaseEventType.success) { Navigator.of(context).pop(); // close paywall on success } }); orca.listEntitlements().then((e) => setState(() => _entitlements = e)); }
@override void dispose() { _sub?.cancel(); super.dispose(); }
@override Widget build(BuildContext context) { return ListView( children: _entitlements.map((e) { return ListTile( title: Text(e.name), onTap: () => orca.purchase( e, externalStore: ExternalStore.stripe, redirectUrl: 'https://yourapp.com/success', failureRedirectUrl: 'https://yourapp.com/failure', ), ); }).toList(), ); }}Server-side work
Section titled “Server-side work”flutter_orca only handles the client side of a purchase. Verifying access on your own backend, cancelling subscriptions remotely, or handling webhooks needs a server SDK:
- TypeScript server SDK for Node backends
- Go server SDK for Go backends