Überarbeitung des Stripe Prozesses

This commit is contained in:
2024-08-23 19:54:55 +02:00
parent 7a286e3519
commit 74d5f92aba
18 changed files with 164 additions and 68 deletions

View File

@@ -7,15 +7,22 @@ import { LoadingService } from '../services/loading.service';
@Injectable()
export class LoadingInterceptor implements HttpInterceptor {
constructor(private loadingService: LoadingService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const hideLoading = request.headers.get('X-Hide-Loading') === 'true';
const requestId = `HTTP-${v4()}`;
this.loadingService.startLoading(requestId, request.url);
if (!hideLoading) {
this.loadingService.startLoading(requestId, request.url);
}
return next.handle(request).pipe(
tap({
finalize: () => this.loadingService.stopLoading(requestId), // Stoppt den Ladevorgang, wenn die Anfrage abgeschlossen ist
// Beachte, dass 'error' und 'complete' hier entfernt wurden, da 'finalize' in allen Fällen aufgerufen wird,
// egal ob die Anfrage erfolgreich war, einen Fehler geworfen hat oder abgeschlossen wurde.
finalize: () => {
if (!hideLoading) {
this.loadingService.stopLoading(requestId);
}
},
}),
);
}

View File

@@ -4,7 +4,7 @@
@if(user){
<a routerLink="/account" class="text-blue-600 border border-blue-600 px-3 py-2 rounded">Account</a>
} @else {
<a routerLink="/pricing" class="text-gray-800">Pricing</a>
<!-- <a routerLink="/pricing" class="text-gray-800">Pricing</a> -->
<a (click)="login()" class="text-blue-600 border border-blue-600 px-3 py-2 rounded">Log In</a>
<a routerLink="/pricing" class="text-white bg-blue-600 px-4 py-2 rounded">Register</a>
}

View File

@@ -43,7 +43,9 @@ export class PricingComponent {
}
} else {
if (priceId) {
this.keycloakService.register({ redirectUri: `${window.location.origin}/pricing/${btoa(priceId)}` });
this.keycloakService.register({
redirectUri: `${window.location.origin}/pricing/${btoa(priceId)}`,
});
} else {
this.keycloakService.register({ redirectUri: `${window.location.origin}/account` });
}

View File

@@ -243,9 +243,7 @@
</div>
</div>
</div>
<div class="flex justify-start">
<button routerLink="/pricing" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">Upgrade Subscription Plan</button>
</div>
<div class="mt-8 sm:hidden">
<h3 class="text-lg font-medium text-gray-700 mb-1">Membership Level</h3>
<div class="space-y-2">
@@ -279,6 +277,16 @@
}
</div>
</div>
@if(user.subscriptionPlan==='free'){
<div class="flex justify-start">
<button
routerLink="/pricing"
class="py-2.5 px-5 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
>
Upgrade Subscription Plan
</button>
</div>
}
</div>
}
</div>

View File

@@ -1,6 +1,6 @@
import { DatePipe, TitleCasePipe } from '@angular/common';
import { ChangeDetectorRef, Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { faTrash } from '@fortawesome/free-solid-svg-icons';
import { NgSelectModule } from '@ng-select/ng-select';
import { initFlowbite } from 'flowbite';
@@ -32,7 +32,7 @@ import { SharedService } from '../../../services/shared.service';
import { SubscriptionsService } from '../../../services/subscriptions.service';
import { UserService } from '../../../services/user.service';
import { SharedModule } from '../../../shared/shared/shared.module';
import { map2User } from '../../../utils/utils';
import { checkAndUpdate, map2User } from '../../../utils/utils';
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
@Component({
selector: 'app-account',
@@ -93,6 +93,7 @@ export class AccountComponent {
private validationMessagesService: ValidationMessagesService,
private subscriptionService: SubscriptionsService,
private datePipe: DatePipe,
private router: Router,
) {}
async ngOnInit() {
setTimeout(() => {
@@ -108,9 +109,11 @@ export class AccountComponent {
}
this.subscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions(this.user.email));
if (this.subscriptions.length === 0) {
this.subscriptions = [{ ended_at: null, start_date: Math.floor(new Date(this.user.created).getTime() / 1000), status: null, metadata: { plan: 'Free Plan' } }];
}
await this.synchronizeSubscriptions(this.subscriptions);
// if (this.subscriptions.length === 0) {
// this.subscriptions = [{ ended_at: null, start_date: Math.floor(new Date(this.user.created).getTime() / 1000), status: null, metadata: { plan: 'Free Plan' } }];
// }
this.profileUrl = this.user.hasProfile ? `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
this.companyLogoUrl = this.user.hasCompanyLogo ? `${this.env.imageBaseUrl}/pictures/logo/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
@@ -128,6 +131,36 @@ export class AccountComponent {
label: this.titleCasePipe.transform(type.name),
}));
}
async synchronizeSubscriptions(subscriptions: StripeSubscription[]) {
let changed = false;
if (this.subscriptions.length === 0) {
if (!this.user.subscriptionPlan) {
this.router.navigate(['pricing']);
} else {
this.subscriptions = [{ ended_at: null, start_date: Math.floor(new Date(this.user.created).getTime() / 1000), status: null, metadata: { plan: 'Free Plan' } }];
changed = checkAndUpdate(changed, this.user.customerType !== 'buyer' && this.user.customerType !== 'seller', () => (this.user.customerType = 'buyer'));
changed = checkAndUpdate(changed, !!this.user.customerSubType, () => (this.user.customerSubType = null));
changed = checkAndUpdate(changed, this.user.subscriptionPlan !== 'free', () => (this.user.subscriptionPlan = 'free'));
changed = checkAndUpdate(changed, !!this.user.subscriptionId, () => (this.user.subscriptionId = null));
}
} else {
const subscription = subscriptions[0];
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.customerType !== 'professional', () => (this.user.customerType = 'professional'));
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.customerSubType !== 'broker', () => (this.user.customerSubType = 'broker'));
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.subscriptionPlan !== 'broker', () => (this.user.subscriptionPlan = 'broker'));
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && !this.user.subscriptionId, () => (this.user.subscriptionId = subscription.id));
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.customerType !== 'professional', () => (this.user.customerType = 'professional'));
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.subscriptionPlan !== 'professional', () => (this.user.subscriptionPlan = 'professional'));
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.subscriptionId !== 'professional', () => (this.user.subscriptionId = subscription.id));
}
if (changed) {
await this.userService.saveGuaranteed(this.user);
this.cdref.detectChanges();
this.cdref.markForCheck();
}
}
ngOnDestroy() {
this.validationMessagesService.clearMessages(); // Löschen Sie alle bestehenden Validierungsnachrichten
}

View File

@@ -1,7 +1,15 @@
<div class="min-h-screen flex items-center justify-center">
<div class="bg-white p-6 rounded-lg shadow-lg max-w-md">
<div class="text-center">
@if(user && (user.subscriptionPlan==='professional' || user.subscriptionPlan==='broker')){
<!-- Neue Bedingung hinzufügen -->
@if(maxAttemptsReached) {
<h2 class="text-2xl font-bold text-red-800 mt-4">We're sorry!</h2>
<p class="text-gray-600 mt-2">
We regret to inform you that we have not yet received any response from our payment service provider regarding the status of your subscription. Please log in to the
<a href="https://www.bizmatch.net" class="text-blue-500 hover:underline">website</a> and check your subscription status under the Account menu. If you have any questions, please contact us at
<a href="mailto:support@bizmatch.net" class="text-blue-500 hover:underline">support&#64;bizmatch.net</a>.
</p>
} @else if(user && (user.subscriptionPlan==='professional' || user.subscriptionPlan==='broker')) {
<svg class="mx-auto h-16 w-16 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>

View File

@@ -16,7 +16,10 @@ import { map2User } from '../../utils/utils';
})
export class SuccessComponent {
user: User;
maxAttemptsReached: boolean = false; // Neue Variable hinzufügen
constructor(private keycloakService: KeycloakService, private userService: UserService, private logService: LogService, private router: Router) {}
async ngOnInit() {
let email = null;
try {
@@ -29,6 +32,7 @@ export class SuccessComponent {
this.checkSubscriptionPlan(email, e.message);
}
}
async checkSubscriptionPlan(email: string, error?: string) {
if (!email) {
this.logService.log({ severity: 'error', text: `Unauthorized Access to Success Page ${error}` });
@@ -44,12 +48,13 @@ export class SuccessComponent {
if (attempts >= maxAttempts) {
clearInterval(intervalId);
console.error('Max attempts reached');
this.maxAttemptsReached = true; // Setze die Variable auf true, wenn die max. Versuche erreicht wurden
return;
}
attempts++;
this.user = await this.userService.getByMail(email);
this.user = await this.userService.getByMail(email, true);
if (this.user && this.user.subscriptionPlan) {
clearInterval(intervalId);

View File

@@ -1,4 +1,4 @@
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { lastValueFrom, Observable } from 'rxjs';
import urlcat from 'urlcat';
@@ -20,12 +20,19 @@ export class UserService {
async save(user: User): Promise<User> {
return await lastValueFrom(this.http.post<User>(`${this.apiBaseUrl}/bizmatch/user`, user));
}
async saveGuaranteed(user: User): Promise<User> {
return await lastValueFrom(this.http.post<User>(`${this.apiBaseUrl}/bizmatch/user/guaranteed`, user));
}
async getById(id: string): Promise<User> {
return await lastValueFrom(this.http.get<User>(`${this.apiBaseUrl}/bizmatch/user/${id}`));
}
async getByMail(mail: string): Promise<User> {
async getByMail(mail: string, hideLoading: boolean = true): Promise<User> {
const url = urlcat(`${this.apiBaseUrl}/bizmatch/user`, { mail });
return await lastValueFrom(this.http.get<User>(url));
let headers = new HttpHeaders();
if (hideLoading) {
headers = headers.set('X-Hide-Loading', 'true');
}
return await lastValueFrom(this.http.get<User>(url, { headers }));
}
async search(criteria?: UserListingCriteria): Promise<ResponseUsersArray> {
return await lastValueFrom(this.http.post<ResponseUsersArray>(`${this.apiBaseUrl}/bizmatch/user/search`, criteria));

View File

@@ -287,6 +287,12 @@ export function assignProperties(target, source) {
}
return target;
}
export function checkAndUpdate(changed: boolean, condition: boolean, assignment: () => any): boolean {
if (condition) {
assignment();
}
return changed || condition;
}
// -----------------------------
// Criteria Proxy
// -----------------------------