diff --git a/src/back-end/CryptEx/CryptExApi/Controllers/UserController.cs b/src/back-end/CryptEx/CryptExApi/Controllers/UserController.cs index ace5bfa..b2be5ec 100644 --- a/src/back-end/CryptEx/CryptExApi/Controllers/UserController.cs +++ b/src/back-end/CryptEx/CryptExApi/Controllers/UserController.cs @@ -182,5 +182,39 @@ public async Task ChangePassword(ChangePasswordDTO changePassword return exceptionHandler.Handle(ex, Request); } } + + [HttpPost("premium")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task GetPremium(BuyPremiumDto dto) + { + try { + var user = await HttpContext.GetUser(); + await userService.GetPremium(user, dto); + + return Ok(); + } catch (Exception ex) { + logger.LogWarning(ex, "Could not get premium."); + return exceptionHandler.Handle(ex, Request); + } + } + + [HttpDelete("premium")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task RemovePremium() + { + try { + var user = await HttpContext.GetUser(); + await userService.RemovePremium(user); + + return Ok(); + } catch (Exception ex) { + logger.LogWarning(ex, "Could not remove premium."); + return exceptionHandler.Handle(ex, Request); + } + } } } diff --git a/src/back-end/CryptEx/CryptExApi/Extensions/ClaimsExtensions.cs b/src/back-end/CryptEx/CryptExApi/Extensions/ClaimsExtensions.cs new file mode 100644 index 0000000..c9a8b79 --- /dev/null +++ b/src/back-end/CryptEx/CryptExApi/Extensions/ClaimsExtensions.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace CryptExApi.Extensions +{ + public static class ClaimsExtensions + { + public static bool IsPremium(this IList claims) => claims.Any(x => x.Type == "premium" && x.Value == bool.TrueString); + } +} diff --git a/src/back-end/CryptEx/CryptExApi/Migrations/20210619170704_Initial.Designer.cs b/src/back-end/CryptEx/CryptExApi/Migrations/20210619215022_Initial.Designer.cs similarity index 99% rename from src/back-end/CryptEx/CryptExApi/Migrations/20210619170704_Initial.Designer.cs rename to src/back-end/CryptEx/CryptExApi/Migrations/20210619215022_Initial.Designer.cs index 53ddebb..bd0a773 100644 --- a/src/back-end/CryptEx/CryptExApi/Migrations/20210619170704_Initial.Designer.cs +++ b/src/back-end/CryptEx/CryptExApi/Migrations/20210619215022_Initial.Designer.cs @@ -10,7 +10,7 @@ namespace CryptExApi.Migrations { [DbContext(typeof(CryptExDbContext))] - [Migration("20210619170704_Initial")] + [Migration("20210619215022_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/src/back-end/CryptEx/CryptExApi/Migrations/20210619170704_Initial.cs b/src/back-end/CryptEx/CryptExApi/Migrations/20210619215022_Initial.cs similarity index 100% rename from src/back-end/CryptEx/CryptExApi/Migrations/20210619170704_Initial.cs rename to src/back-end/CryptEx/CryptExApi/Migrations/20210619215022_Initial.cs diff --git a/src/back-end/CryptEx/CryptExApi/Models/DTO/BuyPremiumDto.cs b/src/back-end/CryptEx/CryptExApi/Models/DTO/BuyPremiumDto.cs new file mode 100644 index 0000000..f38b676 --- /dev/null +++ b/src/back-end/CryptEx/CryptExApi/Models/DTO/BuyPremiumDto.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace CryptExApi.Models.DTO +{ + public class BuyPremiumDto + { + [Required] + public string FirstName { get; set; } + + [Required] + public string LastName { get; set; } + + [CreditCard] + public string CreditCardNumber { get; set; } + + [Required] + public string Expiracy { get; set; } + + [Required] + public string Cvc { get; set; } + } +} diff --git a/src/back-end/CryptEx/CryptExApi/Services/DepositService.cs b/src/back-end/CryptEx/CryptExApi/Services/DepositService.cs index 6c92a17..43effa4 100644 --- a/src/back-end/CryptEx/CryptExApi/Services/DepositService.cs +++ b/src/back-end/CryptEx/CryptExApi/Services/DepositService.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using CryptExApi.Data; +using CryptExApi.Extensions; using CryptExApi.Models.Database; using CryptExApi.Models.SignalR; using CryptExApi.Models.ViewModel; @@ -36,14 +37,16 @@ public class DepositService : IDepositService private readonly IDepositRepository repository; private readonly IStripeRepository stripeRepository; private readonly IWalletRepository walletRepository; + private readonly UserManager userManager; - public DepositService(IConfiguration configuration, IHubContext hubContext, IDepositRepository repository, IStripeRepository stripeRepository, IWalletRepository walletRepository) + public DepositService(IConfiguration configuration, IHubContext hubContext, IDepositRepository repository, IStripeRepository stripeRepository, IWalletRepository walletRepository, UserManager userManager) { this.configuration = configuration; this.hubContext = hubContext; this.repository = repository; this.stripeRepository = stripeRepository; this.walletRepository = walletRepository; + this.userManager = userManager; } public async Task CreatePaymentSession(decimal amount, AppUser user) @@ -84,17 +87,14 @@ public async Task CreatePaymentSession(decimal amount, App break; } - var options = new SessionCreateOptions - { - PaymentMethodTypes = paymentMethods, - LineItems = new List + var lineItems = new List { new SessionLineItemOptions { PriceData = new SessionLineItemPriceDataOptions { Currency = currency, - UnitAmountDecimal = amount * 100, + UnitAmountDecimal = amount * 100m, ProductData = new SessionLineItemPriceDataProductDataOptions { Name = $"Fiat {user.PreferedCurrency} Deposit", @@ -102,7 +102,29 @@ public async Task CreatePaymentSession(decimal amount, App }, Quantity = 1 } - }, + }; + + if (!(await userManager.GetClaimsAsync(user)).IsPremium()) { + lineItems.Add(new SessionLineItemOptions + { + PriceData = new SessionLineItemPriceDataOptions + { + Currency = currency, + UnitAmountDecimal = ((amount * 100m) * 0.02m), + ProductData = new SessionLineItemPriceDataProductDataOptions + { + Name = "Fiat Deposit Fee", + Description = "A 2% transaction fee will be applied to your deposit. Get premium now to have 0 fees on your deposits !" + } + }, + Quantity = 1 + }); + } + + var options = new SessionCreateOptions + { + PaymentMethodTypes = paymentMethods, + LineItems = lineItems, Mode = "payment", SuccessUrl = configuration["BaseUrl"] + "/deposit-withdraw?fiatDepositStatus=success", CancelUrl = configuration["BaseUrl"] + "/deposit-withdraw?fiatDepositStatus=cancelled", diff --git a/src/back-end/CryptEx/CryptExApi/Services/UserService.cs b/src/back-end/CryptEx/CryptExApi/Services/UserService.cs index c1cdb13..4213ed0 100644 --- a/src/back-end/CryptEx/CryptExApi/Services/UserService.cs +++ b/src/back-end/CryptEx/CryptExApi/Services/UserService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using CryptExApi.Exceptions; using CryptExApi.Models.Database; @@ -36,6 +37,10 @@ public interface IUserService Task SetIban(AppUser user, IbanDto dto); Task SetAccountStatus(Guid userId, AccountStatus status); + + Task GetPremium(AppUser user, BuyPremiumDto dto); + + Task RemovePremium(AppUser user); } public class UserService : IUserService @@ -158,5 +163,16 @@ public async Task SetAccountStatus(Guid userId, AccountStatus status) { await userRepository.SetAccountStatus(userId, status); } + + //Did not have the time to make a proper premium subscription (I originally wanted to use Stripe) + public async Task GetPremium(AppUser user, BuyPremiumDto dto) + { + await userManager.AddClaimAsync(user, new Claim("premium", bool.TrueString)); + } + + public async Task RemovePremium(AppUser user) + { + await userManager.RemoveClaimAsync(user, new Claim("premium", bool.TrueString)); + } } } diff --git a/src/front-end/CryptExFrontEnd/src/app/app.module.ts b/src/front-end/CryptExFrontEnd/src/app/app.module.ts index a0ec633..89c0b62 100644 --- a/src/front-end/CryptExFrontEnd/src/app/app.module.ts +++ b/src/front-end/CryptExFrontEnd/src/app/app.module.ts @@ -17,6 +17,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core' import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { AdminModule } from './admin/admin.module'; import { WalletModule } from './wallet/wallet.module'; +import { PremiumModule } from './premium/premium.module'; // AoT requires an exported function for factories export function HttpLoaderFactory(http: HttpClient) { @@ -38,6 +39,7 @@ export function HttpLoaderFactory(http: HttpClient) { DepositWithdrawModule, AdminModule, WalletModule, + PremiumModule, AppRoutingModule, //This must be the last module loaded, otherwise other routes will be ignored. TranslateModule.forRoot({ loader: { diff --git a/src/front-end/CryptExFrontEnd/src/app/deposit-withdraw/components/deposit-withdraw-fiat/deposit-withdraw-fiat.component.html b/src/front-end/CryptExFrontEnd/src/app/deposit-withdraw/components/deposit-withdraw-fiat/deposit-withdraw-fiat.component.html index faa8a86..a5cac20 100644 --- a/src/front-end/CryptExFrontEnd/src/app/deposit-withdraw/components/deposit-withdraw-fiat/deposit-withdraw-fiat.component.html +++ b/src/front-end/CryptExFrontEnd/src/app/deposit-withdraw/components/deposit-withdraw-fiat/deposit-withdraw-fiat.component.html @@ -2,6 +2,7 @@

{{ 'DepositWithdraw.Fiat.Deposit' | translate }}

+

{{ (isPremium() ? 'DepositWithdraw.Fiat.NoFee' : 'DepositWithdraw.Fiat.Fee') | translate }}

diff --git a/src/front-end/CryptExFrontEnd/src/app/main/components/header/header.component.ts b/src/front-end/CryptExFrontEnd/src/app/main/components/header/header.component.ts index b424ebd..5bfbb15 100644 --- a/src/front-end/CryptExFrontEnd/src/app/main/components/header/header.component.ts +++ b/src/front-end/CryptExFrontEnd/src/app/main/components/header/header.component.ts @@ -27,6 +27,10 @@ export class HeaderComponent implements OnInit { return this.authService.IsInRole("admin") } + isPremium(): boolean { + return this.authService.HasClaim("premium"); + } + @HostListener('document:click', ['$event']) onDocumentClick(event: MouseEvent) { var el = event.target as HTMLElement; diff --git a/src/front-end/CryptExFrontEnd/src/app/main/components/home/home.component.html b/src/front-end/CryptExFrontEnd/src/app/main/components/home/home.component.html index 7a142f5..dd01227 100644 --- a/src/front-end/CryptExFrontEnd/src/app/main/components/home/home.component.html +++ b/src/front-end/CryptExFrontEnd/src/app/main/components/home/home.component.html @@ -2,17 +2,17 @@
-

Qui sommes-nous ?

+

{{ 'Home.HeaderSmallLine' | translate }}

- Découvrez le monde des cryptos en quelques minutes + {{ 'Home.HeaderMessage' | translate }}

- Cryptex est une plateforme d'échange entre monnaie FIAT et Crypto + {{ 'Home.HeaderDesc' | translate }}

- Create an account + {{ 'Home.JoinBtn' | translate }}
@@ -47,7 +47,7 @@

- Questions fréquentes + {{ 'Home.Faq.Title' | translate }}

@@ -55,11 +55,10 @@

- Comment déposer des crypto-monnaies sur mon compte ? + {{ 'Home.Faq.Q1' | translate }}

- Pour déposer des crypto-monnaies, vous devez accéder à votre "Wallet", sélectionnez la crypto, sélectionnez - "Déposer".
+ {{ 'Home.Faq.A1' | translate }}

@@ -73,12 +72,10 @@

- Comment déposer des euros sur mon compte ? + {{ 'Home.Faq.Q2' | translate }}

- Vous pouvez effectuer une transaction d'une valeur de 249 EUR (ou l'équivalent) en fournissant les - informations suivantes : Nom complet, adresse électronique, date de naissance, nationalité, adresse de - résidence.
+ {{ 'Home.Faq.A2' | translate }}

@@ -89,7 +86,7 @@

- Best cryptos of the year + {{ 'Home.OurCryptos' | translate }}

@@ -99,27 +96,24 @@

- - -

Loading...

-
+
+

{{ 'Global.Loading' | translate }}

+
+
+

{{ 'Global.LoadError' | translate }}

+

- - - - -

- Pricing + {{ 'Home.Pricing' | translate }}

@@ -129,7 +123,7 @@

- If you're not satisfied, contact us within the first 14 days and we'll send you a full refund. + {{ 'Home.PricingDesc' | translate }}

@@ -141,16 +135,16 @@

- Monthly Premium Membership + {{ 'Premium.PromoCard.Title' | translate }}

- With a premium account you have multiple advantages on our platform + {{ 'Premium.PromoCard.Desc' | translate }}

- What's included + {{ 'Premium.PromoCard.Included' | translate }}

@@ -166,7 +160,7 @@

- Private forum access + {{ 'Premium.PromoCard.PrivForum' | translate }}

@@ -181,7 +175,7 @@

- Member resources + {{ 'Premium.PromoCard.MemResx' | translate }}

@@ -196,7 +190,7 @@

- Entry to annual conference + {{ 'Premium.PromoCard.AnnualConvs' | translate }}

@@ -211,7 +205,7 @@

- Free Transaction + {{ 'Premium.PromoCard.FreeDeposits' | translate }}

@@ -220,80 +214,34 @@

- Pay every month + {{ 'Premium.PromoCard.Pay.Freq' | translate }}

- $49 + 49 - USD + {{ userService.SelectedCurrency }}

- Learn about our membership policy + {{ 'Premium.PromoCard.Pay.Policy' | translate }}

-

- - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/front-end/CryptExFrontEnd/src/app/main/components/home/home.component.ts b/src/front-end/CryptExFrontEnd/src/app/main/components/home/home.component.ts index 35f1ba5..e9f399e 100644 --- a/src/front-end/CryptExFrontEnd/src/app/main/components/home/home.component.ts +++ b/src/front-end/CryptExFrontEnd/src/app/main/components/home/home.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { Subscription } from 'rxjs'; import { AlertType, SnackBarCreate } from 'src/app/components/snackbar/snack-bar'; import { SnackbarService } from 'src/app/services/snackbar.service'; +import { UserService } from 'src/app/user/services/user.service'; import { WalletType, WalletViewModel } from 'src/app/wallet/models/wallet-view-model'; import { WalletService } from 'src/app/wallet/services/wallet.service'; import { CurrencyService } from '../../services/currency.service'; @@ -16,7 +17,7 @@ export class HomeComponent implements OnInit { sub: Subscription; assets: WalletViewModel[]; - constructor(private _walletService: WalletService, private snack: SnackbarService, private curService: CurrencyService) { } + constructor(private _walletService: WalletService, private snack: SnackbarService, public userService: UserService, private curService: CurrencyService) { } ngOnInit(): void { this.loadCurrencies(); @@ -30,8 +31,9 @@ export class HomeComponent implements OnInit { loadCurrencies(): void { this._walletService.GetWalletList().then(x => { if (x.success){ - this.assets= x.content.filter(x => x.type == WalletType.Crypto); + this.assets = x.content.filter(x => x.type == WalletType.Crypto); } else { + this.assets = [] this.snack.ShowSnackbar(new SnackBarCreate("Error", "Could not load crypto-currencies data.", AlertType.Error)); } }); diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.html b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.html new file mode 100644 index 0000000..18b33d6 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.html @@ -0,0 +1,118 @@ +
+
+
+
+
+
+

+ {{ 'Premium.PromoCard.Title' | translate }} +

+

+ {{ 'Premium.PromoCard.Desc' | translate }} +

+
+
+

+ {{ 'Premium.PromoCard.Included' | translate }} +

+
+
+
    +
  • +
    + + +
    +

    + {{ 'Premium.PromoCard.PrivForum' | translate }} +

    +
  • + +
  • +
    + + +
    +

    + {{ 'Premium.PromoCard.MemResx' | translate }} +

    +
  • + +
  • +
    + + +
    +

    + {{ 'Premium.PromoCard.AnnualConvs' | translate }} +

    +
  • + +
  • +
    + + +
    +

    + {{ 'Premium.PromoCard.FreeDeposits' | translate }} +

    +
  • +
+
+
+
+

+ {{ 'Premium.PromoCard.Pay.Freq' | translate }} +

+
+ + 49 + + + {{ userService.SelectedCurrency }} + +
+

+ + {{ 'Premium.PromoCard.Pay.Policy' | translate }} + +

+ +
+
+
+
+
\ No newline at end of file diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.scss b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.spec.ts b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.spec.ts new file mode 100644 index 0000000..280252d --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PremiumHomeComponent } from './premium-home.component'; + +describe('PremiumHomeComponent', () => { + let component: PremiumHomeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PremiumHomeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PremiumHomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.ts b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.ts new file mode 100644 index 0000000..1702c56 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-home/premium-home.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { AuthService } from 'src/app/auth/services/auth.service'; +import { UserService } from 'src/app/user/services/user.service'; + +@Component({ + selector: 'app-premium-home', + templateUrl: './premium-home.component.html', + styleUrls: ['./premium-home.component.scss'] +}) +export class PremiumHomeComponent implements OnInit { + + constructor(private authService: AuthService, public userService: UserService) { } + + ngOnInit(): void { + } + + isPremium(): boolean { + return this.authService.HasClaim("premium"); + } +} diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.html b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.html new file mode 100644 index 0000000..93e0db8 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.html @@ -0,0 +1,8 @@ +

{{ 'Premium.Manage.Title' | translate }}

+ +
+ +
\ No newline at end of file diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.scss b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.spec.ts b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.spec.ts new file mode 100644 index 0000000..b55678a --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PremiumManageComponent } from './premium-manage.component'; + +describe('PremiumManageComponent', () => { + let component: PremiumManageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PremiumManageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PremiumManageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.ts b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.ts new file mode 100644 index 0000000..2c87d8a --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-manage/premium-manage.component.ts @@ -0,0 +1,32 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthService } from 'src/app/auth/services/auth.service'; +import { AlertType, SnackBarCreate } from 'src/app/components/snackbar/snack-bar'; +import { SnackbarService } from 'src/app/services/snackbar.service'; +import { PremiumService } from '../../services/premium.service'; + +@Component({ + selector: 'app-premium-manage', + templateUrl: './premium-manage.component.html', + styleUrls: ['./premium-manage.component.scss'] +}) +export class PremiumManageComponent implements OnInit { + + constructor(private premiumService: PremiumService, private snack: SnackbarService, private authService: AuthService, private router: Router) { } + + ngOnInit(): void { + } + + doCancel(): void { + this.premiumService.CancelPremium().then(async x => { + if (x.success) { + await this.authService.RefreshAccessToken(); + this.router.navigate(['premium']); + this.snack.ShowSnackbar(new SnackBarCreate("Success", "You are now unsubscribed.", AlertType.Success)); + } else { + this.snack.ShowSnackbar(new SnackBarCreate("Error", "Could not subscribe.", AlertType.Error)); + } + }) + } + +} diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.html b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.html new file mode 100644 index 0000000..3f200fa --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.html @@ -0,0 +1,39 @@ +

{{ 'Premium.Pay.Title' | translate }}

+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
\ No newline at end of file diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.scss b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.spec.ts b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.spec.ts new file mode 100644 index 0000000..8f0bbb0 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PremiumPayComponent } from './premium-pay.component'; + +describe('PremiumPayComponent', () => { + let component: PremiumPayComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PremiumPayComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PremiumPayComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.ts b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.ts new file mode 100644 index 0000000..519ae31 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/components/premium-pay/premium-pay.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthService } from 'src/app/auth/services/auth.service'; +import { AlertType, SnackBarCreate } from 'src/app/components/snackbar/snack-bar'; +import { SnackbarService } from 'src/app/services/snackbar.service'; +import { BuyPremiumDto } from '../../models/buy-premium-dto'; +import { PremiumService } from '../../services/premium.service'; + +@Component({ + selector: 'app-premium-pay', + templateUrl: './premium-pay.component.html', + styleUrls: ['./premium-pay.component.scss'] +}) +export class PremiumPayComponent implements OnInit { + dto: BuyPremiumDto = {} as BuyPremiumDto; + + constructor(private premiumService: PremiumService, private snack: SnackbarService, private authService: AuthService, private router: Router) { } + + ngOnInit(): void { + } + + doPay(): void { + this.premiumService.BuyPremium(this.dto).then(async x => { + if (x.success) { + await this.authService.RefreshAccessToken(); + this.router.navigate(['premium']); + this.snack.ShowSnackbar(new SnackBarCreate("Success", "You are now subscribed, thank you !", AlertType.Success)); + } else { + this.snack.ShowSnackbar(new SnackBarCreate("Error", "Could not subscribe.", AlertType.Error)); + } + }) + } +} diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/models/buy-premium-dto.ts b/src/front-end/CryptExFrontEnd/src/app/premium/models/buy-premium-dto.ts new file mode 100644 index 0000000..7c54a51 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/models/buy-premium-dto.ts @@ -0,0 +1,7 @@ +export interface BuyPremiumDto { + firstName: string; + lastName: string; + creditCardNumber: string; + expiracy: string; + cvc: string; +} \ No newline at end of file diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/premium-routing.ts b/src/front-end/CryptExFrontEnd/src/app/premium/premium-routing.ts new file mode 100644 index 0000000..13a48d9 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/premium-routing.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { PremiumGuard } from '../guards/premium.guard'; +import { PremiumHomeComponent } from './components/premium-home/premium-home.component'; +import { PremiumManageComponent } from './components/premium-manage/premium-manage.component'; +import { PremiumPayComponent } from './components/premium-pay/premium-pay.component'; + +const routes: Routes = [ + { + path: 'premium', + component: PremiumHomeComponent + }, + { + path: 'premium/pay', + component: PremiumPayComponent + }, + { + path: 'premium/manage', + component: PremiumManageComponent, + canActivate: [PremiumGuard] + } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class PremiumRouting { } diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/premium.module.ts b/src/front-end/CryptExFrontEnd/src/app/premium/premium.module.ts new file mode 100644 index 0000000..1aab0a7 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/premium.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { PremiumRouting } from "./premium-routing"; +import { PremiumHomeComponent } from './components/premium-home/premium-home.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { PremiumManageComponent } from './components/premium-manage/premium-manage.component'; +import { PremiumPayComponent } from './components/premium-pay/premium-pay.component'; +import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; + + + +@NgModule({ + declarations: [ + PremiumHomeComponent, + PremiumManageComponent, + PremiumPayComponent + ], + imports: [ + CommonModule, + PremiumRouting, + BrowserModule, + FormsModule, + TranslateModule + ] +}) +export class PremiumModule { } diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/services/premium.service.spec.ts b/src/front-end/CryptExFrontEnd/src/app/premium/services/premium.service.spec.ts new file mode 100644 index 0000000..cf0eb36 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/services/premium.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PremiumService } from './premium.service'; + +describe('PremiumService', () => { + let service: PremiumService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PremiumService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/front-end/CryptExFrontEnd/src/app/premium/services/premium.service.ts b/src/front-end/CryptExFrontEnd/src/app/premium/services/premium.service.ts new file mode 100644 index 0000000..ba29ab8 --- /dev/null +++ b/src/front-end/CryptExFrontEnd/src/app/premium/services/premium.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { CustomHttpClientService } from 'src/app/api/custom-http-client/custom-http-client.service'; +import { ApiResult } from 'src/app/api/models/api-result'; +import { BuyPremiumDto } from '../models/buy-premium-dto'; + +@Injectable({ + providedIn: 'root' +}) +export class PremiumService { + + constructor(private http: CustomHttpClientService) { } + + public async BuyPremium(dto: BuyPremiumDto): Promise { + return this.http.Post("User/premium", dto); + } + + public async CancelPremium(): Promise { + return this.http.Delete("User/premium"); + } +} diff --git a/src/front-end/CryptExFrontEnd/src/assets/i18n/en-us.json b/src/front-end/CryptExFrontEnd/src/assets/i18n/en-us.json index bd3bc1c..3e146b9 100644 --- a/src/front-end/CryptExFrontEnd/src/assets/i18n/en-us.json +++ b/src/front-end/CryptExFrontEnd/src/assets/i18n/en-us.json @@ -10,6 +10,7 @@ "MyWallets": "My Wallets", "Wallets": "Wallets", "DepositWithdraw": "Deposit / Withdraw", + "Premium": "Premium", "Admin": "Admin portal", "Contact": "Contact", "OpenUserMenu": "Open user menu" @@ -53,6 +54,57 @@ }, "Copyright": "2021 CryptEx - All rights reserved." }, + "Home": { + "HeaderSmallLine": "Who are we ?", + "HeaderMessage": "Discover the world of cryptocurrencies in a few minutes", + "HeaderDesc": "CryptEx is an exchange platform between Fiat and Crypto-currencies", + "JoinBtn": "Create an account", + "Faq": { + "Title": "Frequently asked questions", + "Q1": "How can I deposit crypto-currencies on my account ?", + "A1": "To deposit crypto-currencies, go to 'Deposit / Withdraw', then click on 'Crypto' then select the crypto-currency you want.", + "Q2": "How can I deposit Euros on my account ?", + "A2": "To deposit Fiat assets, go to 'Deposit / Withdraw', then click on 'Fiat', and enter the amount of your selected currency you want to deposit." + }, + "OurCryptos": "Our crypto-currencies", + "Pricing": "Pricing", + "PricingDesc": "If you're not satisfied, contact us within the first 14 days and we'll send you a full refund." + }, + "Premium": { + "Title": "Premium", + "Pay": { + "Title": "Get premium", + "FirstName": "First name", + "LastName": "Last name", + "CCNumber": "Credit card number", + "Expiracy": "Expiracy date", + "Cvc": "Cvc", + "FirstNamePH": "First name", + "LastNamePH": "Last name", + "CCNumberPH": "Credit card number", + "ExpiracyPH": "Expiracy date (MM/YY)", + "CvcPH": "Cvc (code at the back)", + "Submit": "Subscribe" + }, + "Manage": { + "Title": "Manage subscription", + "Cancel": "Cancel subscription" + }, + "PromoCard": { + "Title": "Monthly Premium Membership", + "Desc": "With a premium account you have multiple advantages on our platform", + "Included": "What's included", + "PrivForum": "Private forum access", + "MemResx": "Member resources", + "AnnualConvs": "Entry to annual conference", + "FreeDeposits": "Free Fiat Deposits", + "Pay": { + "Freq": "Pay every month", + "Policy": "Learn about our membership policy", + "Get": "Get Premium" + } + } + }, "Auth": { "Logout": { "Title": "Log out", @@ -125,6 +177,8 @@ }, "Fiat": { "Deposit": "Deposit fiat", + "NoFee": "As a premium user, your transaction has 0 fee !", + "Fee": "A 2% transaction fee will be applied to your deposit. Get premium now to have 0 fees on your deposits !", "Withdraw": "Withdraw fiat", "Destination": "Transfer destination :", "NoAccount": "You do not have any bank account linked, please go to 'My Account' and link one to withdraw fiat assets.",