MedReport API

Documentation

Documentation API MedReport

Documentation complète pour l'intégration Flutter

🔐 Authentification

Toutes les requêtes API doivent inclure un token Bearer dans l'en-tête Authorization :

final response = await http.get(
  Uri.parse('${baseUrl}/api/endpoint'),
  headers: {
    'Authorization': 'Bearer $token',
    'Accept': 'application/json',
  },
);

Inscription

Créer un compte

POST

Endpoint: /api/register

Corps de la requête:

{
    "name": "John Doe",
    "email": "john@example.com",
    "password": "password123",
    "password_confirmation": "password123",
    "birth_date": "1990-01-01",     // Optionnel
    "phone": "+237600000000",       // Optionnel
    "address": "Yaoundé, Cameroun", // Optionnel
    "speciality": "Cardiologie",    // Optionnel
    "medical_order_number": "12345" // Optionnel
}

Exemple de réponse:

{
    "user": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "phone": "+237600000000",
        "address": "Yaoundé, Cameroun",
        "birth_date": "1990-01-01",
        "speciality": "Cardiologie",
        "medical_order_number": "12345",
        "email_verified_at": null,
        "created_at": "2024-02-14T12:00:00.000000Z",
        "updated_at": "2024-02-14T12:00:00.000000Z"
    },
    "token": "votre_token_d_acces"
}

Exemple d'intégration Flutter:

Future register({
  required String name,
  required String email,
  required String password,
  required String passwordConfirmation,
  String? birthDate,
  String? phone,
  String? address,
  String? speciality,
  String? medicalOrderNumber,
}) async {
  final response = await http.post(
    Uri.parse('${baseUrl}/api/register'),
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'name': name,
      'email': email,
      'password': password,
      'password_confirmation': passwordConfirmation,
      if (birthDate != null) 'birth_date': birthDate,
      if (phone != null) 'phone': phone,
      if (address != null) 'address': address,
      if (speciality != null) 'speciality': speciality,
      if (medicalOrderNumber != null) 'medical_order_number': medicalOrderNumber,
    }),
  );

  if (response.statusCode == 201) {
    final data = jsonDecode(response.body);
    await storage.write(key: 'token', value: data['token']);
    return User.fromJson(data['user']);
  }

  throw Exception(jsonDecode(response.body)['message']);
}

Modèles

Modèle User

class User {
  final int id;
  final String name;
  final String email;
  final String? phone;
  final String? address;
  final String? speciality;
  final String? medicalOrderNumber;
  final DateTime? birthDate;
  final DateTime? emailVerifiedAt;
  final DateTime createdAt;
  final DateTime updatedAt;

  User({
    required this.id,
    required this.name,
    required this.email,
    this.phone,
    this.address,
    this.speciality,
    this.medicalOrderNumber,
    this.birthDate,
    this.emailVerifiedAt,
    required this.createdAt,
    required this.updatedAt,
  });

  factory User.fromJson(Map json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'],
      phone: json['phone'],
      address: json['address'],
      speciality: json['speciality'],
      medicalOrderNumber: json['medical_order_number'],
      birthDate: json['birth_date'] != null
          ? DateTime.parse(json['birth_date'])
          : null,
      emailVerifiedAt: json['email_verified_at'] != null
          ? DateTime.parse(json['email_verified_at'])
          : null,
      createdAt: DateTime.parse(json['created_at']),
      updatedAt: DateTime.parse(json['updated_at']),
    );
  }

  Map toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
      'phone': phone,
      'address': address,
      'speciality': speciality,
      'medical_order_number': medicalOrderNumber,
      'birth_date': birthDate?.toIso8601String(),
      'email_verified_at': emailVerifiedAt?.toIso8601String(),
      'created_at': createdAt.toIso8601String(),
      'updated_at': updatedAt.toIso8601String(),
    };
  }
}

Provider User

class UserProvider extends ChangeNotifier {
  User? _user;
  final storage = const FlutterSecureStorage();

  User? get user => _user;

  Future loadUser() async {
    final token = await storage.read(key: 'token');
    if (token == null) return;

    try {
      final response = await http.get(
        Uri.parse('${baseUrl}/api/user'),
        headers: {
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
        },
      );

      if (response.statusCode == 200) {
        _user = User.fromJson(jsonDecode(response.body));
        notifyListeners();
      }
    } catch (e) {
      await storage.delete(key: 'token');
    }
  }

  Future updateProfile({
    String? name,
    String? phone,
    String? address,
    String? birthDate,
    String? speciality,
    String? medicalOrderNumber,
  }) async {
    final token = await storage.read(key: 'token');
    if (token == null) return;

    final response = await http.put(
      Uri.parse('${baseUrl}/api/user/profile'),
      headers: {
        'Authorization': 'Bearer $token',
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
      body: jsonEncode({
        if (name != null) 'name': name,
        if (phone != null) 'phone': phone,
        if (address != null) 'address': address,
        if (birthDate != null) 'birth_date': birthDate,
        if (speciality != null) 'speciality': speciality,
        if (medicalOrderNumber != null) 'medical_order_number': medicalOrderNumber,
      }),
    );

    if (response.statusCode == 200) {
      _user = User.fromJson(jsonDecode(response.body));
      notifyListeners();
    } else {
      throw Exception('Échec de la mise à jour du profil');
    }
  }
}

Connexion

Se connecter

POST

Endpoint: /api/login

Corps de la requête:

{
    "email": "john@example.com",
    "password": "password123"
}

Exemple d'intégration Flutter:

Future login({
  required String email,
  required String password,
}) async {
  final response = await http.post(
    Uri.parse('${baseUrl}/api/login'),
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'email': email,
      'password': password,
    }),
  );

  if (response.statusCode == 200) {
    final data = jsonDecode(response.body);
    // Sauvegarder le token
    await storage.write(key: 'token', value: data['token']);
    return User.fromJson(data['user']);
  }

  throw Exception(jsonDecode(response.body)['message']);
}

Mot de passe oublié

Demander un lien de réinitialisation

POST

Endpoint: /api/forgot-password

Corps de la requête:

{
    "email": "john@example.com"
}

Exemple d'intégration Flutter:

Future forgotPassword({
  required String email,
}) async {
  final response = await http.post(
    Uri.parse('${baseUrl}/api/forgot-password'),
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'email': email,
    }),
  );

  if (response.statusCode != 200) {
    throw Exception(jsonDecode(response.body)['message']);
  }
}

Réinitialiser le mot de passe

POST

Endpoint: /api/reset-password

Corps de la requête:

{
    "token": "token_reçu_par_email",
    "email": "john@example.com",
    "password": "nouveau_mot_de_passe",
    "password_confirmation": "nouveau_mot_de_passe"
}

Exemple d'intégration Flutter:

Future resetPassword({
  required String token,
  required String email,
  required String password,
  required String passwordConfirmation,
}) async {
  final response = await http.post(
    Uri.parse('${baseUrl}/api/reset-password'),
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'token': token,
      'email': email,
      'password': password,
      'password_confirmation': passwordConfirmation,
    }),
  );

  if (response.statusCode != 200) {
    throw Exception(jsonDecode(response.body)['message']);
  }
}

Déconnexion

Se déconnecter

POST

Endpoint: /api/logout

Exemple d'intégration Flutter:

Future logout() async {
  final token = await storage.read(key: 'token');

  final response = await http.post(
    Uri.parse('${baseUrl}/api/logout'),
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
    },
  );

  if (response.statusCode == 200) {
    // Supprimer le token stocké
    await storage.delete(key: 'token');
    return;
  }

  throw Exception('Erreur lors de la déconnexion');
}

Authentification OAuth

Connexion avec Google

GET

URL de redirection: /api/auth/google

URL de callback: /api/auth/google/callback

Future signInWithGoogle() async {
  final url = Uri.parse('${baseUrl}/api/auth/google');
  // Utiliser url_launcher ou webview pour la redirection
  if (await canLaunch(url.toString())) {
    await launch(url.toString());
  } else {
    throw Exception('Impossible d\'ouvrir l\'URL d\'authentification');
  }
}

Connexion avec Facebook

GET

URL de redirection: /api/auth/facebook

URL de callback: /api/auth/facebook/callback

Future signInWithFacebook() async {
  final url = Uri.parse('${baseUrl}/api/auth/facebook');
  if (await canLaunch(url.toString())) {
    await launch(url.toString());
  } else {
    throw Exception('Impossible d\'ouvrir l\'URL d\'authentification');
  }
}

Connexion avec GitHub

GET

URL de redirection: /api/auth/github

URL de callback: /api/auth/github/callback

Future signInWithGithub() async {
  final url = Uri.parse('${baseUrl}/api/auth/github');
  if (await canLaunch(url.toString())) {
    await launch(url.toString());
  } else {
    throw Exception('Impossible d\'ouvrir l\'URL d\'authentification');
  }
}

👤 Profil Utilisateur

Récupérer le profil

GET

Endpoint: /api/user/profile

Paramètres: Aucun

Exemple de réponse:

{
    "user": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "avatar": "1_1743615420.jpg",
        "phone": "+237600000000",
        "address": "Yaoundé, Cameroun",
        "birth_date": "1990-01-01",
        "speciality": "Cardiologie",
        "medical_order_number": "12345",
        "has_used_trial": true,
        "email_verified_at": "2024-02-14T12:00:00.000000Z",
        "created_at": "2024-02-14T12:00:00.000000Z",
        "updated_at": "2024-02-14T12:00:00.000000Z"
    },
    "message": "Profil récupéré avec succès"
}

Mettre à jour le profil

PUT

Endpoint: /api/user/profile

Corps de la requête (multipart/form-data):

{
    "name": "John Doe",           // Optionnel
    "birth_date": "1990-01-01",   // Optionnel
    "phone": "+237600000000",     // Optionnel
    "address": "Yaoundé, Cameroun", // Optionnel
    "speciality": "Cardiologie",  // Optionnel
    "medical_order_number": "12345", // Optionnel
    "avatar": [FICHIER IMAGE]     // Optionnel, max 2MB
}

Exemple de réponse:

{
    "message": "Profil mis à jour avec succès",
    "user": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "avatar": "1_1743615420.jpg",
        "phone": "+237600000000",
        "address": "Yaoundé, Cameroun",
        "birth_date": "1990-01-01",
        "speciality": "Cardiologie",
        "medical_order_number": "12345",
        "has_used_trial": true,
        "email_verified_at": "2024-02-14T12:00:00.000000Z",
        "created_at": "2024-02-14T12:00:00.000000Z",
        "updated_at": "2024-02-14T12:00:00.000000Z"
    }
}

Mettre à jour l'email

PUT

Endpoint: /api/user/profile/email

Corps de la requête:

{
    "email": "nouveau@example.com",
    "current_password": "mot_de_passe_actuel"
}

Exemple de réponse:

{
    "message": "Adresse email mise à jour avec succès",
    "user": {
        "id": 1,
        "name": "John Doe",
        "email": "nouveau@example.com",
        "email_verified_at": null,
        // autres attributs...
    }
}

Changer le mot de passe

PUT

Endpoint: /api/user/profile/password

Corps de la requête:

{
    "current_password": "mot_de_passe_actuel",
    "password": "nouveau_mot_de_passe",
    "password_confirmation": "nouveau_mot_de_passe"
}

Exemple de réponse:

{
    "message": "Mot de passe mis à jour avec succès"
}

Supprimer l'avatar

DELETE

Endpoint: /api/user/profile/avatar

Paramètres: Aucun

Exemple de réponse:

{
    "message": "Avatar supprimé avec succès",
    "user": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "avatar": null,
        // autres attributs...
    }
}

💳 Abonnements Utilisateur

Récupérer l'abonnement actuel

GET

Endpoint: /api/subscriptions/current

Paramètres: Aucun

Exemple de réponse (avec abonnement):

{
    "message": "Abonnement actif récupéré",
    "subscription": {
        "id": 3,
        "name": "Abonnement Premium",
        "plan_id": 2,
        "plan_name": "Premium",
        "is_trial": false,
        "has_used_trial": true,
        "starts_at": "2024-04-01T00:00:00.000000Z",
        "ends_at": "2024-05-01T00:00:00.000000Z",
        "remaining_days": 29,
        "plan_price": 10000,
        "plan_interval": 30
    },
    "has_active_subscription": true
}

Exemple de réponse (sans abonnement):

{
    "message": "Aucun abonnement actif",
    "subscription": null,
    "has_active_subscription": false
}

Démarrer un essai gratuit

POST

Endpoint: /api/subscriptions/trial

Corps de la requête:

{
    "plan_id": 2
}

Notes:

  • Un utilisateur ne peut activer qu'un seul essai gratuit (tous plans confondus)
  • Un utilisateur ne peut pas avoir plusieurs abonnements actifs (essai ou payant)
  • Le plan doit offrir une période d'essai (configurée par l'administrateur)

Exemple de réponse (succès):

{
    "message": "Période d'essai activée avec succès",
    "status": "success",
    "data": {
        "subscription": {
            "id": 5,
            "subscriber_type": "App\\Models\\User",
            "subscriber_id": 2,
            "plan_id": 2,
            "name": "Trial Subscription",
            "slug": "trial-subscription",
            "description": null,
            "is_trial": true,
            "trial_ends_at": "2024-04-15T00:00:00.000000Z",
            "starts_at": "2024-04-02T00:00:00.000000Z",
            "ends_at": "2024-04-15T00:00:00.000000Z",
            "updated_at": "2024-04-02T17:05:19.000000Z",
            "created_at": "2024-04-02T17:05:19.000000Z"
        },
        "trial_ends_at": "2024-04-15 00:00:00",
        "plan": {
            "id": 2,
            "name": {
                "en": "Premium"
            },
            "description": {
                "en": "Plan premium avec toutes les fonctionnalités"
            },
            "price": 10000,
            "interval": 30,
            "trial_period": 14,
            "created_at": "2024-02-11T09:59:31.000000Z",
            "updated_at": "2024-02-11T09:59:31.000000Z"
        }
    }
}

Exemple de réponse (déjà utilisé un essai):

{
    "message": "Vous avez déjà utilisé votre période d'essai gratuit. Vous ne pouvez plus bénéficier d'essai sur aucun forfait.",
    "status": "error"
}

S'abonner à un plan (paiement)

POST

Endpoint: /api/subscriptions/subscribe

Corps de la requête:

{
    "plan_id": 2
}

Notes:

  • Si l'utilisateur a un essai actif pour le même plan, il sera converti en abonnement payant
  • Un utilisateur ne peut pas s'abonner s'il a déjà un abonnement payant actif à un autre plan
  • Le paiement est géré par NotchPay

Exemple de réponse (succès):

{
    "authorization_url": "https://pay.notchpay.co/rUZp8lTuQuJQyxpb0QzntE3eRPaV6jjgXqU9HjOeb9Sg7riFPCTZx9wmQuleQEHIVVba8pekz4uMIy0EMY0prJqljdHwmYPRlflXIDdrBMYPeNYpAE2ei34XMKzDoAHwBMYesQXRphAYo6cym5gAT18trWSgpl6N84QZ4jyBKva2aNkdvypUgvFnWr9Qfbl",
    "reference": "SUB-2-1743613520",
    "message": "Paiement initialisé avec succès"
}

Résilier un abonnement

POST

Endpoint: /api/subscriptions/unsubscribe

Corps de la requête: Aucun

Exemple de réponse (succès):

{
    "message": "Abonnement résilié avec succès."
}

Historique des abonnements

GET

Endpoint: /api/subscriptions/all

Paramètres: Aucun

Exemple de réponse:

{
    "subscriptions": [
        {
            "id": 2,
            "subscriber_type": "App\\Models\\User",
            "subscriber_id": 2,
            "plan_id": 1,
            "name": "Trial Subscription",
            "slug": "trial-subscription",
            "is_trial": true,
            "trial_ends_at": "2024-03-15T00:00:00.000000Z",
            "starts_at": "2024-03-01T00:00:00.000000Z",
            "ends_at": "2024-03-15T00:00:00.000000Z",
            "is_active": false,
            "is_in_trial": false,
            "plan": {
                "id": 1,
                "name": {
                    "en": "Basic"
                },
                "price": 5000
                // autres attributs...
            }
        },
        {
            "id": 5,
            "subscriber_type": "App\\Models\\User",
            "subscriber_id": 2,
            "plan_id": 2,
            "name": "Abonnement Premium",
            "slug": "abonnement-premium",
            "is_trial": false,
            "trial_ends_at": null,
            "starts_at": "2024-04-02T00:00:00.000000Z",
            "ends_at": "2024-05-02T00:00:00.000000Z",
            "is_active": true,
            "is_in_trial": false,
            "plan": {
                "id": 2,
                "name": {
                    "en": "Premium"
                },
                "price": 10000
                // autres attributs...
            }
        }
    ]
}

👥 Gestion des Utilisateurs

Gestion des Utilisateurs (Administration)

Liste des Utilisateurs

GET

Endpoint: /api/admin/users

Paramètres:

  • page: Numéro de page (défaut: 1)
  • limit: Éléments par page (défaut: 10)
  • search: Terme de recherche pour nom, email, spécialité ou numéro d'ordre
Future> getUsers({
  int page = 1,
  int limit = 10,
  String? search,
}) async {
  final queryParams = {
    'page': page.toString(),
    'limit': limit.toString(),
    if (search != null) 'search': search,
  };

  final response = await http.get(
    Uri.parse('${baseUrl}/api/admin/users').replace(queryParameters: queryParams),
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
    },
  );

  if (response.statusCode == 200) {
    return json.decode(response.body);
  }
  throw Exception('Échec du chargement des utilisateurs');
}

Exemple de réponse:

{
  "data": [
    {
      "id": "1",
      "name": "John Doe",
      "email": "john@example.com",
      "subscription": {
        "plan": "Business",
        "isActive": true,
        "startDate": "2024-02-01T00:00:00.000Z",
        "endDate": "2024-03-01T00:00:00.000Z"
      },
      "status": "active",
      "createdAt": "2024-01-01T00:00:00.000Z",
      "phone": "+237600000000",
      "address": "Yaoundé, Cameroun",
      "speciality": "Cardiologie",
      "medical_order_number": "12345"
    }
  ],
  "total": 50,
  "currentPage": 1,
  "perPage": 10,
  "lastPage": 5
}

Détails d'un Utilisateur

GET

Endpoint: /api/admin/users/{id}

Future> getUserDetails(String userId) async {
  final response = await http.get(
    Uri.parse('${baseUrl}/api/admin/users/$userId'),
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
    },
  );

  if (response.statusCode == 200) {
    return json.decode(response.body);
  }
  throw Exception('Échec du chargement des détails de l\'utilisateur');
}

Exemple de réponse:

{
  "user": {
    "id": "1",
    "name": "John Doe",
    "email": "john@example.com",
    "phone": "+237600000000",
    "address": "Yaoundé, Cameroun",
    "birth_date": "1990-01-01",
    "speciality": "Cardiologie",
    "medical_order_number": "12345",
    "createdAt": "2024-01-01T00:00:00.000Z",
    "status": "active",
    "currentSubscription": {
      "plan": "Business",
      "isActive": true,
      "startDate": "2024-02-01T00:00:00.000Z",
      "endDate": "2024-03-01T00:00:00.000Z"
    },
    "subscriptionHistory": [
      {
        "id": "1",
        "plan": "Business",
        "startDate": "2024-02-01T00:00:00.000Z",
        "endDate": "2024-03-01T00:00:00.000Z",
        "isActive": true,
        "price": 49.99
      }
    ]
  }
}

Modèles

Modèle AdminUser

class AdminUser {
  final String id;
  final String name;
  final String email;
  final String? phone;
  final String? address;
  final String? speciality;
  final String? medicalOrderNumber;
  final String status;
  final DateTime createdAt;
  final UserSubscription? currentSubscription;
  final List subscriptionHistory;

  AdminUser({
    required this.id,
    required this.name,
    required this.email,
    this.phone,
    this.address,
    this.speciality,
    this.medicalOrderNumber,
    required this.status,
    required this.createdAt,
    this.currentSubscription,
    required this.subscriptionHistory,
  });

  factory AdminUser.fromJson(Map json) {
    return AdminUser(
      id: json['id'].toString(),
      name: json['name'],
      email: json['email'],
      phone: json['phone'],
      address: json['address'],
      speciality: json['speciality'],
      medicalOrderNumber: json['medical_order_number'],
      status: json['status'],
      createdAt: DateTime.parse(json['createdAt']),
      currentSubscription: json['currentSubscription'] != null
          ? UserSubscription.fromJson(json['currentSubscription'])
          : null,
      subscriptionHistory: (json['subscriptionHistory'] as List)
          .map((item) => SubscriptionHistory.fromJson(item))
          .toList(),
    );
  }
}

class UserSubscription {
  final String plan;
  final bool isActive;
  final DateTime startDate;
  final DateTime endDate;

  UserSubscription({
    required this.plan,
    required this.isActive,
    required this.startDate,
    required this.endDate,
  });

  factory UserSubscription.fromJson(Map json) {
    return UserSubscription(
      plan: json['plan'],
      isActive: json['isActive'],
      startDate: DateTime.parse(json['startDate']),
      endDate: DateTime.parse(json['endDate']),
    );
  }
}

class SubscriptionHistory {
  final String id;
  final String plan;
  final DateTime startDate;
  final DateTime endDate;
  final bool isActive;
  final double price;

  SubscriptionHistory({
    required this.id,
    required this.plan,
    required this.startDate,
    required this.endDate,
    required this.isActive,
    required this.price,
  });

  factory SubscriptionHistory.fromJson(Map json) {
    return SubscriptionHistory(
      id: json['id'].toString(),
      plan: json['plan'],
      startDate: DateTime.parse(json['startDate']),
      endDate: DateTime.parse(json['endDate']),
      isActive: json['isActive'],
      price: double.parse(json['price'].toString()),
    );
  }
}