Skip to content

Instantly share code, notes, and snippets.

@eliyas5044
Last active May 27, 2024 17:18
Show Gist options
  • Save eliyas5044/aa5bf268c06bb11ac69f627702d48140 to your computer and use it in GitHub Desktop.
Save eliyas5044/aa5bf268c06bb11ac69f627702d48140 to your computer and use it in GitHub Desktop.

Let's use the latest version of the in_app_purchase package in Flutter to implement the API integration for getting courses, a single course, and applying promo codes.

Step 1: Add Dependencies

Ensure your pubspec.yaml file includes the latest in_app_purchase package:

dependencies:
  flutter:
    sdk: flutter
  in_app_purchase_storekit: ^0.3.0
  http: ^0.13.3

Step 2: API Endpoints in Laravel

Implement the Laravel endpoints to fetch course data and apply promo codes.

CoursesController.php

<?php

namespace App\Http\Controllers;

use App\Models\Course;
use App\Models\PromoCode;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class CoursesController extends Controller
{
    public function getCourses()
    {
        $courses = Course::all();
        return response()->json($courses);
    }

    public function getCourse($id)
    {
        $course = Course::find($id);
        if (!$course) {
            return response()->json(['error' => 'Course not found'], 404);
        }
        return response()->json($course);
    }

    public function applyPromoCode(Request $request)
    {
        $code = $request->input('code');
        $promo = PromoCode::where('code', $code)->first();

        if ($promo && $promo->isValid()) {
            return response()->json(['success' => true, 'discount' => $promo->discount]);
        }

        return response()->json(['success' => false, 'message' => 'Invalid promo code'], 400);
    }
}

Step 3: Flutter Integration

Implement the Flutter app to fetch courses, display them, and handle in-app purchases.

Fetching and Displaying Courses

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class Course {
  final String id;
  final String title;
  final String description;
  final double price;

  Course({required this.id, required this.title, required this.description, required this.price});

  factory Course.fromJson(Map<String, dynamic> json) {
    return Course(
      id: json['id'],
      title: json['title'],
      description: json['description'],
      price: json['price'],
    );
  }
}

class CoursesScreen extends StatefulWidget {
  @override
  _CoursesScreenState createState() => _CoursesScreenState();
}

class _CoursesScreenState extends State<CoursesScreen> {
  List<Course> _courses = [];

  @override
  void initState() {
    super.initState();
    _fetchCourses();
  }

  Future<void> _fetchCourses() async {
    final response = await http.get(Uri.parse('https://yourapi.com/api/courses'));
    if (response.statusCode == 200) {
      final List<dynamic> coursesJson = json.decode(response.body);
      setState(() {
        _courses = coursesJson.map((json) => Course.fromJson(json)).toList();
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Courses')),
      body: ListView.builder(
        itemCount: _courses.length,
        itemBuilder: (context, index) {
          final course = _courses[index];
          return ListTile(
            title: Text(course.title),
            subtitle: Text(course.description),
            trailing: Text('\$${course.price}'),
            onTap: () {
              // Navigate to course details and purchase screen
            },
          );
        },
      ),
    );
  }
}

In-App Purchase Integration

Implementing in-app purchases using the in_app_purchase_storekit package.

import 'package:flutter/material.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';

class PurchaseService {
  final InAppPurchase _iap = InAppPurchase.instance;

  void initialize() {
    _iap.purchaseStream.listen(_onPurchaseUpdated);
    _iap.restorePurchases();
  }

  void _onPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
    purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
      if (purchaseDetails.status == PurchaseStatus.pending) {
        // Handle pending state
      } else if (purchaseDetails.status == PurchaseStatus.error) {
        // Handle error state
      } else if (purchaseDetails.status == PurchaseStatus.purchased) {
        // Handle successful purchase
      }
    });
  }

  void buyProduct(ProductDetails productDetails) {
    final PurchaseParam purchaseParam = PurchaseParam(productDetails: productDetails);
    _iap.buyNonConsumable(purchaseParam: purchaseParam);
  }
}

Applying Promo Codes

void _applyPromoCode(String code) async {
  final response = await http.post(
    Uri.parse('https://yourapi.com/api/apply-promo-code'),
    headers: {
      'Authorization': 'Bearer ${your_token}',
      'Content-Type': 'application/json'
    },
    body: json.encode({'code': code}),
  );

  if (response.statusCode == 200) {
    final discount = json.decode(response.body)['discount'];
    // Apply discount logic
  } else {
    // Handle invalid promo code
  }
}

By following these steps, you can implement the required API endpoints and integrate them into your Flutter application with in-app purchases.

To properly organize your Flutter project, you should place the PurchaseService class in a dedicated service file. Here's a suggested structure:

Project Structure

lib/
├── main.dart
├── screens/
│   └── home_screen.dart
├── services/
│   └── purchase_service.dart

PurchaseService Implementation

Create a new file named purchase_service.dart under the services directory:

services/purchase_service.dart

import 'package:in_app_purchase/in_app_purchase.dart';

class PurchaseService {
  final InAppPurchase _iap = InAppPurchase.instance;

  void initialize() {
    _iap.purchaseStream.listen(_onPurchaseUpdated);
    _iap.restorePurchases();
  }

  void _onPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
    purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
      if (purchaseDetails.status == PurchaseStatus.pending) {
        // Handle pending state
      } else if (purchaseDetails.status == PurchaseStatus.error) {
        // Handle error state
      } else if (purchaseDetails.status == PurchaseStatus.purchased) {
        // Handle successful purchase
      }
    });
  }

  void buyProduct(ProductDetails productDetails) {
    final PurchaseParam purchaseParam = PurchaseParam(productDetails: productDetails);
    _iap.buyNonConsumable(purchaseParam: purchaseParam);
  }
}

Using PurchaseService in HomeScreen

screens/home_screen.dart

import 'package:flutter/material.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import '../services/purchase_service.dart';

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final PurchaseService _purchaseService = PurchaseService();
  List<ProductDetails> _products = [];

  @override
  void initState() {
    super.initState();
    _purchaseService.initialize();
    _loadProducts();
  }

  void _loadProducts() async {
    final bool available = await InAppPurchase.instance.isAvailable();
    if (!available) {
      // Handle store not available
      return;
    }

    const Set<String> _kIds = {'product_id_1', 'product_id_2'};
    final ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails(_kIds);
    if (response.notFoundIDs.isNotEmpty) {
      // Handle missing product IDs
    }

    setState(() {
      _products = response.productDetails;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Courses')),
      body: ListView.builder(
        itemCount: _products.length,
        itemBuilder: (context, index) {
          final product = _products[index];
          return ListTile(
            title: Text(product.title),
            subtitle: Text(product.description),
            trailing: Text('${product.price}'),
            onTap: () => _purchaseService.buyProduct(product),
          );
        },
      ),
    );
  }
}

By organizing your project this way, you ensure that the PurchaseService is reusable and keeps your code clean and modular.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment