Initialer Commit: Projekt Start
This commit is contained in:
132
lib/services/analytics_service.dart
Normal file
132
lib/services/analytics_service.dart
Normal file
@@ -0,0 +1,132 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
/// Analytics Service für Umami Integration
|
||||
///
|
||||
/// Umami ist eine datenschutzfreundliche Analytics-Lösung.
|
||||
/// Konfiguriere deine Umami-URL und Website-ID in den statischen Variablen.
|
||||
class AnalyticsService {
|
||||
// Ersetze mit deiner Umami-Instanz URL
|
||||
static const String umamiUrl = 'https://analytics.hyggecraftery.com';
|
||||
|
||||
// Ersetze mit deiner Website-ID von Umami
|
||||
static const String websiteId = 'c43cd023-ff64-43c8-a42a-ac23d32366f6';
|
||||
|
||||
// Optionale API-Key für Authentifizierung
|
||||
static const String? apiKey = null;
|
||||
|
||||
/// Sendet ein Event an Umami
|
||||
static Future<void> trackEvent({
|
||||
required String eventName,
|
||||
Map<String, dynamic>? eventData,
|
||||
String? url,
|
||||
String? referrer,
|
||||
}) async {
|
||||
try {
|
||||
final uri = Uri.parse('$umamiUrl/api/send');
|
||||
|
||||
final payload = {
|
||||
'website': websiteId,
|
||||
'hostname': 'hyggecraftery.com',
|
||||
'url': url ?? '/',
|
||||
'referrer': referrer,
|
||||
'name': eventName,
|
||||
'data': eventData ?? {},
|
||||
};
|
||||
|
||||
final headers = <String, String>{
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
if (apiKey != null) {
|
||||
headers['Authorization'] = 'Bearer $apiKey';
|
||||
}
|
||||
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: headers,
|
||||
body: json.encode(payload),
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
print('Umami Tracking Fehler: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
// Fail silently - Analytics sollten die App nicht blockieren
|
||||
print('Analytics Fehler: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Trackt einen Page View
|
||||
static Future<void> trackPageView(String page) async {
|
||||
await trackEvent(
|
||||
eventName: 'pageview',
|
||||
url: page,
|
||||
);
|
||||
}
|
||||
|
||||
/// Trackt eine Produktansicht
|
||||
static Future<void> trackProductView(int productId, String productName) async {
|
||||
await trackEvent(
|
||||
eventName: 'product_view',
|
||||
eventData: {
|
||||
'product_id': productId,
|
||||
'product_name': productName,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Trackt einen "Zum Warenkorb hinzufügen" Event
|
||||
static Future<void> trackAddToCart(int productId, String productName, double price) async {
|
||||
await trackEvent(
|
||||
eventName: 'add_to_cart',
|
||||
eventData: {
|
||||
'product_id': productId,
|
||||
'product_name': productName,
|
||||
'price': price,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Trackt einen Checkout-Start
|
||||
static Future<void> trackCheckoutStart(double total) async {
|
||||
await trackEvent(
|
||||
eventName: 'checkout_start',
|
||||
eventData: {
|
||||
'total': total,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Trackt eine erfolgreiche Bestellung
|
||||
static Future<void> trackPurchase(int orderId, double total) async {
|
||||
await trackEvent(
|
||||
eventName: 'purchase',
|
||||
eventData: {
|
||||
'order_id': orderId,
|
||||
'total': total,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Trackt eine Suche
|
||||
static Future<void> trackSearch(String searchQuery) async {
|
||||
await trackEvent(
|
||||
eventName: 'search',
|
||||
eventData: {
|
||||
'query': searchQuery,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Trackt eine Kategorie-Ansicht
|
||||
static Future<void> trackCategoryView(String categoryName) async {
|
||||
await trackEvent(
|
||||
eventName: 'category_view',
|
||||
eventData: {
|
||||
'category': categoryName,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
285
lib/services/auth_service.dart
Normal file
285
lib/services/auth_service.dart
Normal file
@@ -0,0 +1,285 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/user.dart';
|
||||
import 'woocommerce_service.dart';
|
||||
|
||||
class AuthService {
|
||||
static const String _userKey = 'user_data';
|
||||
static const String _tokenKey = 'auth_token';
|
||||
|
||||
/// Meldet einen Benutzer an
|
||||
///
|
||||
/// Verwendet die WordPress REST API für die Authentifizierung
|
||||
Future<User?> login(String username, String password) async {
|
||||
try {
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/jwt-auth/v1/token');
|
||||
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode({
|
||||
'username': username,
|
||||
'password': password,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
|
||||
if (data['token'] != null) {
|
||||
// Speichere Token
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_tokenKey, data['token']);
|
||||
|
||||
// Hole Benutzerdaten
|
||||
final user = await getUserData(data['token']);
|
||||
|
||||
if (user != null) {
|
||||
await saveUser(user);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
} else if (response.statusCode == 403) {
|
||||
// JWT Auth Plugin nicht installiert, verwende alternative Methode
|
||||
return await _loginAlternative(username, password);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
print('Login-Fehler: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Alternative Login-Methode über WooCommerce REST API
|
||||
Future<User?> _loginAlternative(String username, String password) async {
|
||||
try {
|
||||
// Verwende Basic Auth mit WooCommerce API
|
||||
final credentials = base64Encode(utf8.encode('$username:$password'));
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wc/v3/customers');
|
||||
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {
|
||||
'Authorization': 'Basic $credentials',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// Suche nach dem Benutzer mit dieser E-Mail/Username
|
||||
final customers = json.decode(response.body) as List;
|
||||
final customer = customers.firstWhere(
|
||||
(c) => c['email'] == username || c['username'] == username,
|
||||
orElse: () => null,
|
||||
);
|
||||
|
||||
if (customer != null) {
|
||||
final user = User.fromJson(customer);
|
||||
await saveUser(user);
|
||||
// Speichere Credentials für spätere API-Calls
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('username', username);
|
||||
await prefs.setString('password', password); // In Produktion verschlüsseln!
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
print('Alternative Login-Fehler: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Holt Benutzerdaten mit Token
|
||||
Future<User?> getUserData(String token) async {
|
||||
try {
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wp/v2/users/me');
|
||||
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
return User.fromJson(data);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
print('Fehler beim Abrufen der Benutzerdaten: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Speichert Benutzerdaten lokal
|
||||
Future<void> saveUser(User user) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_userKey, json.encode(user.toJson()));
|
||||
}
|
||||
|
||||
/// Lädt gespeicherten Benutzer
|
||||
Future<User?> getSavedUser() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final userJson = prefs.getString(_userKey);
|
||||
|
||||
if (userJson != null) {
|
||||
final userData = json.decode(userJson);
|
||||
return User.fromJson(userData);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
print('Fehler beim Laden des gespeicherten Benutzers: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Prüft ob Benutzer eingeloggt ist
|
||||
Future<bool> isLoggedIn() async {
|
||||
final user = await getSavedUser();
|
||||
return user != null;
|
||||
}
|
||||
|
||||
/// Meldet den Benutzer ab
|
||||
Future<void> logout() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_userKey);
|
||||
await prefs.remove(_tokenKey);
|
||||
await prefs.remove('username');
|
||||
await prefs.remove('password');
|
||||
}
|
||||
|
||||
/// Holt das gespeicherte Token
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString(_tokenKey);
|
||||
}
|
||||
|
||||
/// Registriert einen neuen Benutzer
|
||||
Future<Map<String, dynamic>> register({
|
||||
required String email,
|
||||
required String username,
|
||||
required String password,
|
||||
String? firstName,
|
||||
String? lastName,
|
||||
}) async {
|
||||
try {
|
||||
// Versuche WordPress REST API
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wp/v2/users');
|
||||
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode({
|
||||
'username': username,
|
||||
'email': email,
|
||||
'password': password,
|
||||
'first_name': firstName ?? '',
|
||||
'last_name': lastName ?? '',
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
final data = json.decode(response.body);
|
||||
return {'success': true, 'user_id': data['id']};
|
||||
} else {
|
||||
final error = json.decode(response.body);
|
||||
return {
|
||||
'success': false,
|
||||
'message': error['message'] ?? 'Registrierung fehlgeschlagen',
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
'success': false,
|
||||
'message': 'Fehler bei der Registrierung: $e',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Sendet Passwort-Reset-E-Mail
|
||||
Future<Map<String, dynamic>> requestPasswordReset(String email) async {
|
||||
try {
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/bdpwr/v1/reset-password');
|
||||
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode({'email': email}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {'success': true, 'message': 'Reset-Link wurde per E-Mail gesendet'};
|
||||
} else {
|
||||
// Fallback: WordPress Standard
|
||||
final uri2 = Uri.parse('${WooCommerceService.baseUrl}/wp-login.php?action=lostpassword');
|
||||
final response2 = await http.post(
|
||||
uri2,
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: 'user_login=$email',
|
||||
);
|
||||
|
||||
return {
|
||||
'success': response2.statusCode == 200,
|
||||
'message': 'Bitte prüfe deine E-Mails für den Reset-Link',
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
'success': false,
|
||||
'message': 'Fehler beim Anfordern des Passwort-Resets: $e',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Aktualisiert Benutzerprofil
|
||||
Future<User?> updateProfile({
|
||||
required String firstName,
|
||||
required String lastName,
|
||||
String? email,
|
||||
String? displayName,
|
||||
}) async {
|
||||
try {
|
||||
final token = await getToken();
|
||||
if (token == null) return null;
|
||||
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wp/v2/users/me');
|
||||
|
||||
final body = <String, dynamic>{
|
||||
'first_name': firstName,
|
||||
'last_name': lastName,
|
||||
};
|
||||
|
||||
if (email != null) body['email'] = email;
|
||||
if (displayName != null) body['name'] = displayName;
|
||||
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: json.encode(body),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
final user = User.fromJson(data);
|
||||
await saveUser(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
print('Fehler beim Aktualisieren des Profils: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
82
lib/services/coupon_service.dart
Normal file
82
lib/services/coupon_service.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../models/coupon.dart';
|
||||
import 'woocommerce_service.dart';
|
||||
|
||||
class CouponService {
|
||||
/// Validiert einen Gutschein-Code
|
||||
Future<Coupon> validateCoupon(String code) async {
|
||||
try {
|
||||
final queryParams = {
|
||||
'code': code,
|
||||
'status': 'publish',
|
||||
'consumer_key': WooCommerceService.consumerKey,
|
||||
'consumer_secret': WooCommerceService.consumerSecret,
|
||||
};
|
||||
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wc/v3/coupons').replace(
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
|
||||
if (data.isNotEmpty) {
|
||||
final couponData = data[0];
|
||||
return Coupon(
|
||||
code: couponData['code'] ?? code,
|
||||
description: couponData['description'],
|
||||
discountType: couponData['discount_type'] ?? 'fixed_cart',
|
||||
amount: couponData['amount'] ?? '0',
|
||||
isValid: true,
|
||||
);
|
||||
} else {
|
||||
return Coupon(
|
||||
code: code,
|
||||
discountType: 'fixed_cart',
|
||||
amount: '0',
|
||||
isValid: false,
|
||||
errorMessage: 'Gutschein-Code nicht gefunden',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return Coupon(
|
||||
code: code,
|
||||
discountType: 'fixed_cart',
|
||||
amount: '0',
|
||||
isValid: false,
|
||||
errorMessage: 'Fehler beim Validieren des Gutscheins',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
return Coupon(
|
||||
code: code,
|
||||
discountType: 'fixed_cart',
|
||||
amount: '0',
|
||||
isValid: false,
|
||||
errorMessage: 'Fehler: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Berechnet den Rabatt für einen Warenkorb
|
||||
double calculateDiscount(Coupon coupon, double cartTotal) {
|
||||
if (!coupon.isValid) return 0.0;
|
||||
|
||||
final amount = double.tryParse(coupon.amount) ?? 0.0;
|
||||
|
||||
switch (coupon.discountType) {
|
||||
case 'percent':
|
||||
case 'percent_product':
|
||||
return (cartTotal * amount) / 100;
|
||||
case 'fixed_cart':
|
||||
case 'fixed_product':
|
||||
return amount;
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
154
lib/services/order_service.dart
Normal file
154
lib/services/order_service.dart
Normal file
@@ -0,0 +1,154 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/order.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import 'woocommerce_service.dart';
|
||||
|
||||
class OrderService {
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
/// Holt alle Bestellungen des eingeloggten Benutzers
|
||||
Future<List<Order>> getOrders({int perPage = 20, int page = 1}) async {
|
||||
try {
|
||||
// Versuche zuerst mit JWT Token
|
||||
final token = await _authService.getToken();
|
||||
|
||||
if (token != null) {
|
||||
return await _getOrdersWithToken(token, perPage, page);
|
||||
}
|
||||
|
||||
// Fallback: Verwende WooCommerce Customer API
|
||||
return await _getOrdersWithCredentials(perPage, page);
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Bestellungen: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Order>> _getOrdersWithToken(String token, int perPage, int page) async {
|
||||
try {
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wc/v3/orders').replace(
|
||||
queryParameters: {
|
||||
'per_page': perPage.toString(),
|
||||
'page': page.toString(),
|
||||
'customer': 'me', // Holt Bestellungen des aktuellen Benutzers
|
||||
},
|
||||
);
|
||||
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((json) => Order.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Fehler: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
// Fallback zur Credentials-Methode
|
||||
return await _getOrdersWithCredentials(perPage, page);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Order>> _getOrdersWithCredentials(int perPage, int page) async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final username = prefs.getString('username');
|
||||
final password = prefs.getString('password');
|
||||
|
||||
if (username == null || password == null) {
|
||||
throw Exception('Nicht eingeloggt');
|
||||
}
|
||||
|
||||
// Hole Customer ID
|
||||
final customerId = await _getCustomerId(username, password);
|
||||
if (customerId == null) {
|
||||
throw Exception('Kunde nicht gefunden');
|
||||
}
|
||||
|
||||
final credentials = base64Encode(utf8.encode('$username:$password'));
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wc/v3/orders').replace(
|
||||
queryParameters: {
|
||||
'per_page': perPage.toString(),
|
||||
'page': page.toString(),
|
||||
'customer': customerId.toString(),
|
||||
},
|
||||
);
|
||||
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {
|
||||
'Authorization': 'Basic $credentials',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((json) => Order.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Fehler: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Bestellungen: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<int?> _getCustomerId(String username, String password) async {
|
||||
try {
|
||||
final credentials = base64Encode(utf8.encode('$username:$password'));
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wc/v3/customers');
|
||||
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {
|
||||
'Authorization': 'Basic $credentials',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final customers = json.decode(response.body) as List;
|
||||
final customer = customers.firstWhere(
|
||||
(c) => c['email'] == username || c['username'] == username,
|
||||
orElse: () => null,
|
||||
);
|
||||
|
||||
return customer != null ? customer['id'] : null;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Fehler beim Abrufen der Customer ID: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Holt Details einer einzelnen Bestellung
|
||||
Future<Order> getOrder(int orderId) async {
|
||||
try {
|
||||
final token = await _authService.getToken();
|
||||
|
||||
if (token != null) {
|
||||
final uri = Uri.parse('${WooCommerceService.baseUrl}/wp-json/wc/v3/orders/$orderId');
|
||||
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
return Order.fromJson(data);
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception('Nicht autorisiert');
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Bestellung: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
149
lib/services/woocommerce_checkout_service.dart
Normal file
149
lib/services/woocommerce_checkout_service.dart
Normal file
@@ -0,0 +1,149 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../providers/cart_provider.dart';
|
||||
import 'woocommerce_service.dart';
|
||||
|
||||
/// Service für WooCommerce Checkout-Funktionalität
|
||||
///
|
||||
/// Dieser Service bietet Methoden für den Checkout-Prozess.
|
||||
/// Für die einfachste Lösung wird die WebView-basierte Checkout-Seite verwendet,
|
||||
/// die alle WooCommerce-Einstellungen automatisch nutzt.
|
||||
class WooCommerceCheckoutService {
|
||||
static const String baseUrl = WooCommerceService.baseUrl;
|
||||
static const String consumerKey = WooCommerceService.consumerKey;
|
||||
static const String consumerSecret = WooCommerceService.consumerSecret;
|
||||
|
||||
/// Erstellt eine Checkout-URL mit Warenkorb-Produkten
|
||||
///
|
||||
/// Diese Methode erstellt eine URL, die direkt zur WooCommerce Checkout-Seite
|
||||
/// führt und die Produkte aus dem Warenkorb automatisch hinzufügt.
|
||||
static String buildCheckoutUrl(CartProvider cart) {
|
||||
if (cart.items.isEmpty) {
|
||||
return '$baseUrl/checkout/';
|
||||
}
|
||||
|
||||
// Erstelle eine URL, die alle Produkte zum Warenkorb hinzufügt
|
||||
// Format: /checkout/?add-to-cart=ID1,ID2,ID3
|
||||
final productIds = cart.items
|
||||
.map((item) => '${item.product.id}:${item.quantity}')
|
||||
.join(',');
|
||||
|
||||
// Alternative: Verwende die WooCommerce Cart-Seite und leite dann weiter
|
||||
// Oder direkt zur Checkout-Seite mit Produkten
|
||||
return '$baseUrl/checkout/?add-to-cart=${cart.items.map((item) => item.product.id).join(',')}';
|
||||
}
|
||||
|
||||
/// Erstellt eine Order über die WooCommerce REST API
|
||||
///
|
||||
/// Diese Methode kann verwendet werden, wenn du einen nativen Checkout
|
||||
/// implementieren möchtest. Sie erfordert Write-Berechtigungen für die API Keys.
|
||||
///
|
||||
/// Hinweis: Für die einfachste Lösung wird die WebView-basierte Checkout-Seite
|
||||
/// empfohlen, da sie alle WooCommerce-Einstellungen (Zahlungen, Versand, etc.)
|
||||
/// automatisch nutzt.
|
||||
static Future<Map<String, dynamic>> createOrder({
|
||||
required CartProvider cart,
|
||||
required Map<String, dynamic> billing,
|
||||
Map<String, dynamic>? shipping,
|
||||
String? paymentMethod,
|
||||
}) async {
|
||||
try {
|
||||
final lineItems = cart.items.map((item) {
|
||||
return {
|
||||
'product_id': item.product.id,
|
||||
'quantity': item.quantity,
|
||||
};
|
||||
}).toList();
|
||||
|
||||
final orderData = {
|
||||
'payment_method': paymentMethod ?? 'bacs',
|
||||
'payment_method_title': paymentMethod ?? 'Direktüberweisung',
|
||||
'set_paid': false,
|
||||
'billing': billing,
|
||||
'shipping': shipping ?? billing,
|
||||
'line_items': lineItems,
|
||||
'shipping_lines': [],
|
||||
};
|
||||
|
||||
final uri = Uri.parse('$baseUrl/wp-json/wc/v3/orders');
|
||||
|
||||
// Basic Auth für API
|
||||
final credentials = base64Encode(utf8.encode('$consumerKey:$consumerSecret'));
|
||||
final headers = {
|
||||
'Authorization': 'Basic $credentials',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: headers,
|
||||
body: json.encode(orderData),
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
return json.decode(response.body);
|
||||
} else {
|
||||
throw Exception('Fehler beim Erstellen der Bestellung: ${response.statusCode} - ${response.body}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Checkout: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Ruft verfügbare Zahlungsmethoden ab
|
||||
static Future<List<Map<String, dynamic>>> getPaymentMethods() async {
|
||||
try {
|
||||
final uri = Uri.parse('$baseUrl/wp-json/wc/v3/payment_gateways');
|
||||
|
||||
final credentials = base64Encode(utf8.encode('$consumerKey:$consumerSecret'));
|
||||
final headers = {
|
||||
'Authorization': 'Basic $credentials',
|
||||
};
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data
|
||||
.where((gateway) => gateway['enabled'] == true)
|
||||
.map((gateway) => {
|
||||
return {
|
||||
'id': gateway['id'],
|
||||
'title': gateway['title'],
|
||||
'description': gateway['description'],
|
||||
};
|
||||
})
|
||||
.toList()
|
||||
.cast<Map<String, dynamic>>();
|
||||
} else {
|
||||
throw Exception('Fehler beim Laden der Zahlungsmethoden: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Zahlungsmethoden: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Ruft Versandoptionen ab
|
||||
static Future<List<Map<String, dynamic>>> getShippingMethods() async {
|
||||
try {
|
||||
final uri = Uri.parse('$baseUrl/wp-json/wc/v3/shipping/zones');
|
||||
|
||||
final credentials = base64Encode(utf8.encode('$consumerKey:$consumerSecret'));
|
||||
final headers = {
|
||||
'Authorization': 'Basic $credentials',
|
||||
};
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.cast<Map<String, dynamic>>();
|
||||
} else {
|
||||
throw Exception('Fehler beim Laden der Versandoptionen: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Versandoptionen: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
230
lib/services/woocommerce_service.dart
Normal file
230
lib/services/woocommerce_service.dart
Normal file
@@ -0,0 +1,230 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../models/product.dart';
|
||||
import '../models/category.dart';
|
||||
import '../models/review.dart';
|
||||
|
||||
class WooCommerceService {
|
||||
// Ersetze diese URL mit deiner WooCommerce Shop-URL
|
||||
static const String baseUrl = 'https://hyggecraftery.com';
|
||||
static const String consumerKey = 'ck_ae583aa69ca3f962e14ae03faadd302937b8f3e2'; // Ersetze mit deinem Consumer Key
|
||||
static const String consumerSecret = 'cs_3835315e5dc3c7e87c89a733e6c3e5ec8f76b70e'; // Ersetze mit deinem Consumer Secret
|
||||
|
||||
// Öffentliche Zugriffe für andere Services
|
||||
static String get shopUrl => baseUrl;
|
||||
|
||||
// Für öffentliche Produkte (ohne Authentifizierung)
|
||||
// Du kannst auch die WooCommerce REST API ohne Keys verwenden, wenn die Produkte öffentlich sind
|
||||
static const String apiBaseUrl = '$baseUrl/wp-json/wc/v3';
|
||||
|
||||
/// Baut eine authentifizierte URL für WooCommerce API-Aufrufe
|
||||
Uri _buildAuthenticatedUrl(String endpoint, Map<String, String>? queryParams) {
|
||||
final params = <String, String>{
|
||||
'consumer_key': consumerKey,
|
||||
'consumer_secret': consumerSecret,
|
||||
if (queryParams != null) ...queryParams,
|
||||
};
|
||||
|
||||
return Uri.parse('$apiBaseUrl/$endpoint').replace(
|
||||
queryParameters: params,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Product>> getProducts({
|
||||
int perPage = 20,
|
||||
int page = 1,
|
||||
String? category,
|
||||
String? search,
|
||||
String? orderBy,
|
||||
String? order,
|
||||
double? minPrice,
|
||||
double? maxPrice,
|
||||
}) async {
|
||||
try {
|
||||
final queryParams = {
|
||||
'per_page': perPage.toString(),
|
||||
'page': page.toString(),
|
||||
'status': 'publish',
|
||||
};
|
||||
|
||||
if (category != null) {
|
||||
queryParams['category'] = category;
|
||||
}
|
||||
|
||||
if (search != null && search.isNotEmpty) {
|
||||
queryParams['search'] = search;
|
||||
}
|
||||
|
||||
if (orderBy != null) {
|
||||
queryParams['orderby'] = orderBy;
|
||||
}
|
||||
|
||||
if (order != null) {
|
||||
queryParams['order'] = order;
|
||||
}
|
||||
|
||||
if (minPrice != null) {
|
||||
queryParams['min_price'] = minPrice.toString();
|
||||
}
|
||||
|
||||
if (maxPrice != null) {
|
||||
queryParams['max_price'] = maxPrice.toString();
|
||||
}
|
||||
|
||||
final uri = _buildAuthenticatedUrl('products', queryParams);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((json) => Product.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Fehler beim Laden der Produkte: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Produkte: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Product> getProduct(int productId) async {
|
||||
try {
|
||||
final uri = _buildAuthenticatedUrl('products/$productId', null);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
return Product.fromJson(data);
|
||||
} else {
|
||||
throw Exception('Fehler beim Laden des Produkts: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen des Produkts: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Product>> getFeaturedProducts({int limit = 6}) async {
|
||||
try {
|
||||
final uri = _buildAuthenticatedUrl('products', {
|
||||
'featured': 'true',
|
||||
'per_page': limit.toString(),
|
||||
'status': 'publish',
|
||||
});
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((json) => Product.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Fehler beim Laden der Featured Produkte: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Featured Produkte: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Holt alle Kategorien
|
||||
Future<List<Category>> getCategories({int? parent}) async {
|
||||
try {
|
||||
final queryParams = <String, String>{
|
||||
'per_page': '100',
|
||||
'hide_empty': 'false',
|
||||
};
|
||||
|
||||
if (parent != null) {
|
||||
queryParams['parent'] = parent.toString();
|
||||
}
|
||||
|
||||
final uri = _buildAuthenticatedUrl('products/categories', queryParams);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((json) => Category.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Fehler beim Laden der Kategorien: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Kategorien: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Holt Bewertungen für ein Produkt
|
||||
Future<List<Review>> getProductReviews(int productId, {int perPage = 10, int page = 1}) async {
|
||||
try {
|
||||
final uri = _buildAuthenticatedUrl('products/reviews', {
|
||||
'product': productId.toString(),
|
||||
'per_page': perPage.toString(),
|
||||
'page': page.toString(),
|
||||
'status': 'approved',
|
||||
});
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((json) => Review.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Fehler beim Laden der Bewertungen: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Bewertungen: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Holt Produktbewertungs-Statistiken
|
||||
Future<ProductRating> getProductRating(int productId) async {
|
||||
try {
|
||||
final product = await getProduct(productId);
|
||||
|
||||
// WooCommerce speichert Rating-Daten im Produkt
|
||||
final averageRating = double.tryParse(product.rating?.toString() ?? '0') ?? 0.0;
|
||||
final ratingCount = product.ratingCount ?? 0;
|
||||
|
||||
return ProductRating(
|
||||
averageRating: averageRating,
|
||||
ratingCount: ratingCount,
|
||||
ratingBreakdown: {}, // Kann aus Reviews berechnet werden
|
||||
);
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Abrufen der Bewertungsstatistik: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Erstellt eine neue Bewertung
|
||||
Future<Review?> createReview({
|
||||
required int productId,
|
||||
required String reviewer,
|
||||
required String reviewerEmail,
|
||||
required String review,
|
||||
required int rating,
|
||||
}) async {
|
||||
try {
|
||||
final uri = _buildAuthenticatedUrl('products/reviews', null);
|
||||
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode({
|
||||
'product_id': productId,
|
||||
'reviewer': reviewer,
|
||||
'reviewer_email': reviewerEmail,
|
||||
'review': review,
|
||||
'rating': rating,
|
||||
'status': 'pending', // Wird vom Admin genehmigt
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
final data = json.decode(response.body);
|
||||
return Review.fromJson(data);
|
||||
} else {
|
||||
throw Exception('Fehler beim Erstellen der Bewertung: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Fehler beim Erstellen der Bewertung: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user