499 lines
22 KiB
C#
499 lines
22 KiB
C#
using gehGassi.Core.Interfaces;
|
|
using gehGassi.Domain.Vouchers;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using gehGassi.Domain.Common;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Security.Cryptography;
|
|
using NetTopologySuite.Algorithm;
|
|
using static Org.BouncyCastle.Asn1.Cmp.Challenge;
|
|
|
|
namespace gehGassi.Core.Services
|
|
{
|
|
/// <summary>
|
|
/// Service der die Verwaltung von Gutscheinkampagnen übernimmt
|
|
/// </summary>
|
|
public class VoucherCampaignService : ServiceBase<VoucherCampaign>, IVoucherCampaignService
|
|
{
|
|
private readonly IRepository<VoucherCampaignWithNames> _namesRepository;
|
|
private readonly IRepository<Voucher> _vouchersRepository;
|
|
private readonly IRepository<VoucherUsage> _voucherUsagesRepository;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
|
|
public VoucherCampaignService(IUnitOfWork unitOfWork) : base(unitOfWork)
|
|
{
|
|
_namesRepository = unitOfWork.GetRepository<VoucherCampaignWithNames>();
|
|
_vouchersRepository = unitOfWork.GetRepository<Voucher>();
|
|
_voucherUsagesRepository = unitOfWork.GetRepository<VoucherUsage>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine gefilterte Liste von Gutschein-Kampagnen zurück
|
|
/// </summary>
|
|
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
|
|
/// <returns>List betroffener Gutschein-Kampagnen</returns>
|
|
public IQueryable<VoucherCampaign> Filter(string filter)
|
|
{
|
|
return Repository.Query(c => c.Title.Contains(filter) || c.NameLocalized.Contains(filter));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellen einer Gutschein-Kampagne
|
|
/// </summary>
|
|
/// <returns>Gutschein-Kampagne</returns>
|
|
public VoucherCampaign Create()
|
|
{
|
|
var item = new VoucherCampaign()
|
|
{
|
|
Id = Guid.NewGuid().ToString("N"),
|
|
Created = DateTimeOffset.UtcNow
|
|
};
|
|
return item;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine gefilterte Liste von Gutschein-Kampagnen zurück.
|
|
/// </summary>
|
|
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
|
|
/// <returns>List betroffener Gutschein-Kampagnen</returns>
|
|
public async Task<List<VoucherCampaign>> SearchAsync(string filter)
|
|
{
|
|
return (await Repository.FindAsync(c => c.Title.Contains(filter) || c.NameLocalized.Contains(filter))).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine gefilterte Liste von Gutschein-Kampagnen mit Namen zurück in einer gewählten Sprache
|
|
/// </summary>
|
|
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
|
|
/// <param name="language">Gewünschte Sprache</param>
|
|
/// <param name="fallback">Fallback Sprache</param>
|
|
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
|
|
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
|
|
/// <returns>Liste betroffener Gutschein-Kampagnen</returns>
|
|
public IQueryable<VoucherCampaignWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
|
|
{
|
|
language = language.ToUpperInvariant();
|
|
fallback = fallback.ToUpperInvariant();
|
|
|
|
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_VoucherCampignsWithNames({0},{1})", c => c.Title.Contains(filter) || c.Name.Contains(filter) || c.CustomerName.Contains(filter), language, fallback);
|
|
|
|
if (!includeDeleted)
|
|
baseQuery = baseQuery.Where(c => c.Deleted == false);
|
|
|
|
if (excludeDraft)
|
|
baseQuery = baseQuery.Where(c => c.Status != VoucherCampaignStatus.Draft);
|
|
|
|
return baseQuery;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine gefilterte Liste von Gutschein-Kampagnen mit Namen für Kunden zurück in einer gewählten Sprache
|
|
/// </summary>
|
|
/// <param name="customerId">Id des Kunden</param>
|
|
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
|
|
/// <param name="language">Gewünschte Sprache</param>
|
|
/// <param name="fallback">Fallback Sprache</param>
|
|
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
|
|
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
|
|
/// <returns>Liste betroffener Gutschein-Kampagnen</returns>
|
|
public IQueryable<VoucherCampaignWithNames> FilterWithNamesCustomer(long customerId, string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
|
|
{
|
|
language = language.ToUpperInvariant();
|
|
fallback = fallback.ToUpperInvariant();
|
|
|
|
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_VoucherCampignsWithNames({0},{1})", c => c.CustomerId == customerId && (c.Title.Contains(filter) || c.Name.Contains(filter) || c.CustomerName.Contains(filter)), language, fallback);
|
|
|
|
if (!includeDeleted)
|
|
baseQuery = baseQuery.Where(c => c.Deleted == false);
|
|
|
|
if (excludeDraft)
|
|
baseQuery = baseQuery.Where(c => c.Status != VoucherCampaignStatus.Draft);
|
|
|
|
return baseQuery;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt die Anzahl von Gutschein-Kampagnen zurück
|
|
/// </summary>
|
|
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
|
|
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
|
|
/// <returns>Anzahl Gutschein-Kampagnen</returns>
|
|
public async Task<int> CountAsync(bool includeDeleted, bool excludeDraft)
|
|
{
|
|
var query = Repository.Query(c => c.Id != "");
|
|
if (!includeDeleted)
|
|
query = query.Where(c => c.Deleted == false);
|
|
if (excludeDraft)
|
|
query = query.Where(c => c.Status != VoucherCampaignStatus.Draft);
|
|
return await query.CountAsync().ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt die Anzahl von Gutschein-Kampagnen in einem Status zurück
|
|
/// </summary>
|
|
/// <param name="status">Gesuchter Status</param>
|
|
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
|
|
/// <returns>Anzahl Gutschein-Kampagnen</returns>
|
|
public async Task<int> CountAsync(VoucherCampaignStatus status, bool includeDeleted)
|
|
{
|
|
var query = Repository.Query(c => c.Status == status);
|
|
if (!includeDeleted)
|
|
query = query.Where(c => c.Deleted == false);
|
|
return await query.CountAsync().ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Gutschein-Kampagne für einen Artikel im Warenkorb zurück
|
|
/// </summary>
|
|
/// <param name="cartItemId">Id des Artikels im Warenkorb</param>
|
|
/// <returns>Gutschein-Kampagne oder null, wenn nicht gefunden</returns>
|
|
public async Task<VoucherCampaign> GetByCartItemAsync(long cartItemId)
|
|
{
|
|
return await Repository.FirstOrDefaultAsync(c => c.CartItemId == cartItemId && c.Status == VoucherCampaignStatus.Draft);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt einen Gutschein-Kampagne für einen Artikel in einer Bestellung zurück
|
|
/// </summary>
|
|
/// <param name="orderItemId">Id des Artikels in der Bestellung</param>
|
|
/// <returns>Gutschein-Kampagne oder null, wenn nicht gefunden</returns>
|
|
public async Task<VoucherCampaign> GetByOrderItemAsync(long orderItemId)
|
|
{
|
|
return await Repository.FirstOrDefaultAsync(c => c.OrderItemId == orderItemId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Setzen des Status von Gutschein-Kampagnen nach Bestellung
|
|
/// </summary>
|
|
/// <param name="orderId">Id der Bestellung</param>
|
|
/// <param name="status">Zu setzender Status</param>
|
|
/// <returns>Anzahl betroffener Gutschein-Kampagnen</returns>
|
|
public async Task<int> SetStatusByOrderAsync(long orderId, VoucherCampaignStatus status)
|
|
{
|
|
var items = await Repository.FindAsync(c => c.OrderId == orderId);
|
|
foreach (var item in items)
|
|
{
|
|
item.Status = status;
|
|
}
|
|
|
|
return items.Count();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Setzen des Bezahlstatus von Gutschein-Kampagnen nach Bestellung
|
|
/// </summary>
|
|
/// <param name="orderId">Id der Bestellung</param>
|
|
/// <param name="status">Zu setzender Status</param>
|
|
/// <returns>Anzahl betroffener Gutschein-Kampagnen</returns>
|
|
public async Task<int> SetPaymentStatusByOrderAsync(long orderId, PaymentStatus status)
|
|
{
|
|
var items = await Repository.FindAsync(c => c.OrderId == orderId);
|
|
foreach (var item in items)
|
|
{
|
|
item.PaymentStatus = status;
|
|
}
|
|
|
|
return items.Count();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starten von gebuchten Gutschein-Kampagnen wenn diese das Start-Datum erreicht haben
|
|
/// </summary>
|
|
/// <returns>Anzahl gestartete Gutschein-Kampagnen</returns>
|
|
public async Task<int> StartBookedAsnyc()
|
|
{
|
|
var items = await Repository.FindAsync(c => c.Status == VoucherCampaignStatus.Booked && c.Deleted == false && c.StartDate <= DateTimeOffset.UtcNow);
|
|
foreach (var item in items)
|
|
{
|
|
item.Status = VoucherCampaignStatus.Running;
|
|
}
|
|
|
|
return items.Count();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stoppen von abgelaufenen Gutschein-Kampagnen wenn diese das Enddatum erreicht haben
|
|
/// </summary>
|
|
/// <returns>Anzahl gestoppte Gutschein-Kampagnen</returns>
|
|
public async Task<int> StopRunningAsync()
|
|
{
|
|
var items = await Repository.FindAsync(c => (c.Status == VoucherCampaignStatus.Running && c.Deleted == false && c.BudgetUsed >= c.Budget) || (c.Status == VoucherCampaignStatus.Running && c.Deleted == false && c.EndDate.Value <= DateTimeOffset.UtcNow));
|
|
foreach (var item in items)
|
|
{
|
|
item.EndDate = DateTimeOffset.UtcNow;
|
|
item.Status = VoucherCampaignStatus.Ended;
|
|
}
|
|
|
|
return items.Count();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellen von Gutscheinen für eine Gutschein-Kampagne
|
|
/// </summary>
|
|
/// <param name="voucherCampaignId">Id der Kampagne</param>
|
|
/// <param name="userName">Der Benutzer der die Gutscheine angelegt hat</param>
|
|
/// <returns>Anzahl der erstellten Gutscheine</returns>
|
|
public async Task<int> CreateVouchersForCampaign(string voucherCampaignId, string userName)
|
|
{
|
|
var campaign = await Repository.FirstOrDefaultAsync(c => c.Id == voucherCampaignId);
|
|
if (campaign != null)
|
|
{
|
|
int sufficientBufferSizeInBytes = (8 * 6 + 7) / 8;
|
|
|
|
for (var i = 0; i < campaign.NumberOfVouchers; i++)
|
|
{
|
|
var buffer = new byte[sufficientBufferSizeInBytes];
|
|
buffer = RandomNumberGenerator.GetBytes(8);
|
|
string code = Convert.ToBase64String(buffer).Substring(0, 8);
|
|
|
|
var voucher = new Voucher()
|
|
{
|
|
Id = Guid.NewGuid().ToString("N"),
|
|
VoucherCampaignId = campaign.Id,
|
|
Code = $"{campaign.Prefix}-" + GenerateManualCode(8),
|
|
QrCode = Guid.NewGuid().ToString("N").ToUpperInvariant(),
|
|
UsedCount = 0,
|
|
VoucherAmmount = campaign.VoucherAmmount,
|
|
IsValid = true
|
|
};
|
|
_vouchersRepository.Add(voucher);
|
|
if (i % 100 == 0)
|
|
{
|
|
await CommitAsync(userName, noAudit: true);
|
|
}
|
|
}
|
|
|
|
await CommitAsync(userName, noAudit: true);
|
|
return campaign.NumberOfVouchers;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prüft ob ein Präfix für eine Gutschein-Kampagne verfügbar ist
|
|
/// </summary>
|
|
/// <param name="prefix">Präfix der geprüft werden soll</param>
|
|
/// <param name="voucherCampaignId">Id der Kampagne die ignoriert werden soll</param>
|
|
/// <returns>true wenn verfügbar, false sonst</returns>
|
|
public async Task<bool> IsPrefixAvailableAsync(string prefix, string voucherCampaignId)
|
|
{
|
|
var itemFound = await Repository.FirstOrDefaultAsync(c => c.Id != voucherCampaignId && c.Prefix == prefix).ConfigureAwait(false);
|
|
return itemFound == null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aktualisiert den Gutschein-Betrag für alle noch nicht eingelösten oder deaktivierten Gutscheine einer Kampagne
|
|
/// </summary>
|
|
/// <param name="voucherCampaignId">Id der Kampagne</param>
|
|
/// <param name="voucherAmmount">Wert der gesetzt werden soll</param>
|
|
/// <returns>Anzahl Gutscheine die betroffen sind</returns>
|
|
public async Task<int> UpdateVoucherAmmountAsync(string voucherCampaignId, decimal voucherAmmount)
|
|
{
|
|
var vouchers = await _vouchersRepository.FindAsync(c => c.VoucherCampaignId == voucherCampaignId && c.UsedCount == 0 && c.IsValid == true);
|
|
foreach (var voucher in vouchers)
|
|
{
|
|
voucher.VoucherAmmount = voucherAmmount;
|
|
}
|
|
return vouchers.Count();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löschen der Gutscheine und deren Verwendung für eine Kampagne
|
|
/// </summary>
|
|
/// <param name="voucherCampaignId">Id der Kampagne</param>
|
|
/// <returns>Anzahl gelöscht</returns>
|
|
public async Task<int> RemoveVouchersAsync(string voucherCampaignId)
|
|
{
|
|
var vouchers = await _vouchersRepository.FindAsync(c => c.VoucherCampaignId == voucherCampaignId);
|
|
foreach (var voucher in vouchers)
|
|
{
|
|
var usages = await _voucherUsagesRepository.FindAsync(c => c.VoucherId == voucher.Id);
|
|
foreach (var usage in usages)
|
|
{
|
|
_voucherUsagesRepository.Remove(usage);
|
|
}
|
|
_vouchersRepository.Remove(voucher);
|
|
}
|
|
return vouchers.Count();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste von Gutscheinen für eine Kampagne zurück
|
|
/// </summary>
|
|
/// <param name="voucherCampaignId">Id der Kampagne</param>
|
|
/// <returns>Liste Gutscheine</returns>
|
|
public async Task<List<Voucher>> GetVouchersForCampaignAsync(string voucherCampaignId)
|
|
{
|
|
var vouchers = await _vouchersRepository.FindAsync(c => c.VoucherCampaignId == voucherCampaignId).ConfigureAwait(false);
|
|
return vouchers.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prüft ob ein manueller Gutscheincode für einen Benutzer gültig ist und eingesetzt werden kann
|
|
/// </summary>
|
|
/// <param name="code">Code</param>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <returns>Validierungs-Ergebnis</returns>
|
|
public async Task<VoucherValidationResult> ValidateVoucherCodeAsync(string code, string appUserId)
|
|
{
|
|
var voucher = await _vouchersRepository.FirstOrDefaultAsync(c => c.Code == code).ConfigureAwait(false);
|
|
if (voucher != null)
|
|
{
|
|
var voucherCampaign = await Repository.FirstOrDefaultAsync(c => c.Id == voucher.VoucherCampaignId).ConfigureAwait(false);
|
|
if (voucherCampaign != null)
|
|
{
|
|
//Läuft die Kampange noch nicht?
|
|
if(voucherCampaign.Status < VoucherCampaignStatus.Running)
|
|
return VoucherValidationResult.CampaignNotStarted;
|
|
|
|
//Ist die Kampagne schon beendet?
|
|
if (voucherCampaign.Status > VoucherCampaignStatus.Running)
|
|
return VoucherValidationResult.CampaignEnded;
|
|
|
|
//Wurde das Budget der Kampagne schon aufgebraucht?
|
|
if (voucherCampaign.BudgetUsed >= voucherCampaign.Budget)
|
|
return VoucherValidationResult.CampaignBudgetUsedUp;
|
|
|
|
//Ist der Gutschein noch gültig?
|
|
if(voucher.IsValid == false)
|
|
return VoucherValidationResult.VoucherAlreadyUsed;
|
|
|
|
//Wurde der Gutschein schon zu oft verwendet?
|
|
var usedByUserCount = await _voucherUsagesRepository.CountAsync(c => c.VoucherId == voucher.Id && c.AppUserId == appUserId).ConfigureAwait(false);
|
|
if(usedByUserCount >= voucherCampaign.MaxUsagePerUser)
|
|
return VoucherValidationResult.MaxUsageReached;
|
|
|
|
return VoucherValidationResult.Valid;
|
|
}
|
|
}
|
|
|
|
return VoucherValidationResult.Invalid;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt einen Gutschein anhand des Codes zurück
|
|
/// </summary>
|
|
/// <param name="code">Code des Gutscheins</param>
|
|
/// <returns>Gutschein oder null, wenn nicht gefunden</returns>
|
|
public async Task<Voucher> GetVoucherByCodeAsync(string code)
|
|
{
|
|
var voucher = await _vouchersRepository.FirstOrDefaultAsync(c => c.Code == code).ConfigureAwait(false);
|
|
return voucher;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt einen Gutschein anhand des QR-Codes zurück
|
|
/// </summary>
|
|
/// <param name="qrCode">QR-Code des Gutscheins</param>
|
|
/// <returns>Gutschein oder null, wenn nicht gefunden</returns>
|
|
public async Task<Voucher> GetVoucherByQrCodeAsync(string qrCode)
|
|
{
|
|
var voucher = await _vouchersRepository.FirstOrDefaultAsync(c => c.QrCode == qrCode).ConfigureAwait(false);
|
|
return voucher;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reserviert einen Gutschein für einen Benutzer und einen Walk
|
|
/// </summary>
|
|
/// <param name="voucherId">Id des Vouchers</param>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <param name="walkId">Id des Walks</param>
|
|
/// <param name="voucherAmmount">Wert des Gutscheins</param>
|
|
/// <param name="voucherAmmountUsed">Wert der verbraucht wurde. Kann kleiner Gutscheinwert sein</param>
|
|
/// <returns>VoucherUsage</returns>
|
|
public async Task<VoucherUsage> ReserveVoucherAsync(string voucherId, string appUserId, string walkId, decimal voucherAmmount, decimal voucherAmmountUsed)
|
|
{
|
|
var voucherUsage = new VoucherUsage()
|
|
{
|
|
VoucherId = voucherId,
|
|
AppUserId = appUserId,
|
|
WalkId = walkId,
|
|
VoucherAmmount = voucherAmmount,
|
|
VoucherAmmountUsed = voucherAmmountUsed,
|
|
UsageStatus = VoucherUsageStatus.Reserved,
|
|
Created = DateTimeOffset.UtcNow
|
|
};
|
|
_voucherUsagesRepository.Add(voucherUsage);
|
|
return voucherUsage;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löschen der Verwendung eines Gutscheins
|
|
/// </summary>
|
|
/// <param name="voucherUsage">VoucherUsage</param>
|
|
public void RemoveUsage(VoucherUsage voucherUsage)
|
|
{
|
|
_voucherUsagesRepository.Remove(voucherUsage);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt die Verwendung eines Gutscheins zurück
|
|
/// </summary>
|
|
/// <param name="voucherId">Id des Gutscheins</param>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <param name="walkId">Id des Walks</param>
|
|
/// <returns>VoucherUsage oder null, wenn nicht gefunden</returns>
|
|
public async Task<VoucherUsage> GetUsageAsync(string voucherId, string appUserId, string walkId)
|
|
{
|
|
var voucherUsage = await _voucherUsagesRepository.FirstOrDefaultAsync(c => c.VoucherId == voucherId && c.AppUserId == appUserId && c.WalkId == walkId).ConfigureAwait(false);
|
|
return voucherUsage;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt einen Gutschein zurück
|
|
/// </summary>
|
|
/// <param name="voucherId">Id des Gutscheins</param>
|
|
/// <returns>Voucher oder null, wenn nicht gefunden</returns>
|
|
public async Task<Voucher> GetVoucherAsync(string voucherId)
|
|
{
|
|
var voucher = await _vouchersRepository.FirstOrDefaultAsync(c => c.Id == voucherId).ConfigureAwait(false);
|
|
return voucher;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löschen von Voucher-Usages für einen AppUser
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <returns>Anzahl gelöscht</returns>
|
|
public async Task<int> RemoveVoucherUsageForAppUserAsync(string appUserId)
|
|
{
|
|
var usages = await _voucherUsagesRepository.FindAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
|
|
foreach (var voucherUsage in usages)
|
|
{
|
|
_voucherUsagesRepository.Remove(voucherUsage);
|
|
}
|
|
return usages.Count();
|
|
}
|
|
|
|
|
|
#region Helper
|
|
|
|
/// <summary>
|
|
/// Erstellt einen zufälligen Code in einer bestimmten Länge
|
|
/// </summary>
|
|
/// <param name="length">Länge des Codes</param>
|
|
/// <returns>Code</returns>
|
|
private string GenerateManualCode(int length)
|
|
{
|
|
var sufficientBufferSizeInBytes = (length * 6 + 7) / 8;
|
|
var buffer = RandomNumberGenerator.GetBytes(sufficientBufferSizeInBytes);
|
|
var code = Convert.ToBase64String(buffer)[..length];
|
|
code = code.ToUpperInvariant();
|
|
var finalCode = $"{code.Substring(0,length / 2)}-{code.Substring(length/2, length/2)}";
|
|
if (finalCode.Contains("+") || finalCode.Contains("/") || finalCode.Contains("="))
|
|
{
|
|
return GenerateManualCode(length);
|
|
}
|
|
return finalCode;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|