Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 157f097dfb | |||
| 630e74814b | |||
| 6248d03ead | |||
| 9b474a398b | |||
| 3deae15f5a | |||
| 8e1df7938b | |||
| d9f2d32c76 | |||
| 9296f0331e | |||
| 1ace61baa5 | |||
| e3e49b8db2 | |||
| 2ed15b548f | |||
| 7d2bc7f1f2 | |||
| ef2428f8e3 | |||
| 5edff5e272 | |||
| b424b24c2e | |||
| 310c1237b1 |
@@ -12,8 +12,8 @@ public sealed class CustomerConfiguration : IEntityTypeConfiguration<Customer>
|
|||||||
builder.Property(c => c.Company).IsRequired(false);
|
builder.Property(c => c.Company).IsRequired(false);
|
||||||
builder.Property(c => c.VatNumber).IsRequired(false);
|
builder.Property(c => c.VatNumber).IsRequired(false);
|
||||||
builder.Property(c => c.Email).IsRequired();
|
builder.Property(c => c.Email).IsRequired();
|
||||||
builder.Property(c => c.Phone).IsRequired();
|
builder.Property(c => c.Phone).IsRequired(false);
|
||||||
builder.Property(c => c.Website).IsRequired();
|
builder.Property(c => c.Website).IsRequired(false);
|
||||||
builder.Property(c => c.Enabled).HasDefaultValue(true);
|
builder.Property(c => c.Enabled).HasDefaultValue(true);
|
||||||
|
|
||||||
builder.OwnsMany(f => f.SocialMedia, b => { b.ToJson(); });
|
builder.OwnsMany(f => f.SocialMedia, b => { b.ToJson(); });
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
using LiteCharms.Features.Abstractions;
|
||||||
|
using LiteCharms.Features.Browser;
|
||||||
using LiteCharms.Features.MidrandBooks.Abstractions;
|
using LiteCharms.Features.MidrandBooks.Abstractions;
|
||||||
|
|
||||||
namespace LiteCharms.Features.MidrandBooks.Extensions;
|
namespace LiteCharms.Features.MidrandBooks.Extensions;
|
||||||
|
|
||||||
public static class Shop
|
public static class Shop
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddShopServices(this IServiceCollection services)
|
public static IServiceCollection AddShopServices(this IServiceCollection services, bool includeLocalStorage = false)
|
||||||
{
|
{
|
||||||
var serviceType = typeof(IService);
|
var serviceType = typeof(IService);
|
||||||
|
|
||||||
@@ -19,6 +20,9 @@ public static class Shop
|
|||||||
|
|
||||||
foreach (var coreImplementation in coreImplementations) services.AddScoped(coreImplementation);
|
foreach (var coreImplementation in coreImplementations) services.AddScoped(coreImplementation);
|
||||||
|
|
||||||
|
if (includeLocalStorage)
|
||||||
|
services.AddScoped<LocalStorageService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
using LiteCharms.Features.Browser;
|
||||||
using LiteCharms.Features.Browser;
|
|
||||||
using LiteCharms.Features.Hasher;
|
using LiteCharms.Features.Hasher;
|
||||||
using LiteCharms.Features.MidrandBooks.Authors.Models;
|
using LiteCharms.Features.MidrandBooks.Authors.Models;
|
||||||
using LiteCharms.Features.MidrandBooks.Payments.Models;
|
using LiteCharms.Features.MidrandBooks.Payments.Models;
|
||||||
@@ -7,7 +6,7 @@ using LiteCharms.Features.MidrandBooks.Products.Models;
|
|||||||
|
|
||||||
namespace LiteCharms.Features.MidrandBooks.Payments;
|
namespace LiteCharms.Features.MidrandBooks.Payments;
|
||||||
|
|
||||||
public sealed class CartService(LocalStorageService localStorage) : IService
|
public sealed class CartService(LocalStorageService localStorage)
|
||||||
{
|
{
|
||||||
private readonly string CartStorageKey = HashService.ToMd5Hash(nameof(Cart)).Value;
|
private readonly string CartStorageKey = HashService.ToMd5Hash(nameof(Cart)).Value;
|
||||||
|
|
||||||
|
|||||||
@@ -147,33 +147,66 @@ public sealed partial class PayfastService(IDbContextFactory<MidrandBooksDbConte
|
|||||||
{
|
{
|
||||||
var pfOutput = new StringBuilder();
|
var pfOutput = new StringBuilder();
|
||||||
|
|
||||||
foreach (var kvp in data)
|
var mandatorySequence = GetPayfastMandatoryFieldSequence();
|
||||||
|
|
||||||
|
foreach (string key in mandatorySequence)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(kvp.Value))
|
if (data.TryGetValue(key, out string? rawValue) && !string.IsNullOrEmpty(rawValue))
|
||||||
continue;
|
{
|
||||||
|
string encodedVal = HttpUtility.UrlEncode(rawValue.Trim());
|
||||||
|
string val = PercentEncodingRegex.Replace(encodedVal, m => m.Value.ToUpperInvariant());
|
||||||
|
|
||||||
string key = kvp.Key;
|
pfOutput.Append($"{key}={val}&");
|
||||||
|
}
|
||||||
string encodedVal = HttpUtility.UrlEncode(kvp.Value.Trim());
|
|
||||||
|
|
||||||
string val = PercentEncodingRegex.Replace(encodedVal, m => m.Value.ToLowerInvariant());
|
|
||||||
|
|
||||||
pfOutput.Append($"{key}={val}&");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string getString = pfOutput.Length > 0
|
var getString = pfOutput.Length > 0
|
||||||
? pfOutput.ToString()[..^1]
|
? pfOutput.ToString()[..^1]
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(passPhrase))
|
if (!string.IsNullOrWhiteSpace(passPhrase))
|
||||||
{
|
{
|
||||||
string encodedPassphrase = HttpUtility.UrlEncode(passPhrase.Trim());
|
string encodedPassphrase = HttpUtility.UrlEncode(passPhrase.Trim());
|
||||||
|
string safePassphrase = PercentEncodingRegex.Replace(encodedPassphrase, m => m.Value.ToUpperInvariant());
|
||||||
string safePassphrase = PercentEncodingRegex.Replace(encodedPassphrase, m => m.Value.ToLowerInvariant());
|
|
||||||
|
|
||||||
getString += $"&passphrase={safePassphrase}";
|
getString += $"&passphrase={safePassphrase}";
|
||||||
}
|
}
|
||||||
|
|
||||||
return HashService.ToMd5Hash(getString);
|
return HashService.ToMd5Hash(getString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string[] GetPayfastMandatoryFieldSequence() =>
|
||||||
|
[
|
||||||
|
"merchant_id",
|
||||||
|
"merchant_key",
|
||||||
|
"return_url",
|
||||||
|
"cancel_url",
|
||||||
|
"notify_url",
|
||||||
|
"name_first",
|
||||||
|
"name_last",
|
||||||
|
"email_address",
|
||||||
|
"cell_number",
|
||||||
|
"m_payment_id",
|
||||||
|
"amount",
|
||||||
|
"item_name",
|
||||||
|
"item_description",
|
||||||
|
"custom_int1",
|
||||||
|
"custom_int2",
|
||||||
|
"custom_int3",
|
||||||
|
"custom_int4",
|
||||||
|
"custom_int5",
|
||||||
|
"custom_str1",
|
||||||
|
"custom_str2",
|
||||||
|
"custom_str3",
|
||||||
|
"custom_str4",
|
||||||
|
"custom_str5",
|
||||||
|
"email_confirmation",
|
||||||
|
"confirmation_address",
|
||||||
|
"payment_method",
|
||||||
|
"subscription_type",
|
||||||
|
"billing_date",
|
||||||
|
"recurring_amount",
|
||||||
|
"frequency",
|
||||||
|
"cycles"
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
+1290
File diff suppressed because it is too large
Load Diff
+54
@@ -0,0 +1,54 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class OnlyEmailIsMandatoryOnCustomer : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "Website",
|
||||||
|
table: "Customers",
|
||||||
|
type: "text",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "text");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "Phone",
|
||||||
|
table: "Customers",
|
||||||
|
type: "text",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "text");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "Website",
|
||||||
|
table: "Customers",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "",
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "text",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "Phone",
|
||||||
|
table: "Customers",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "",
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "text",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-3
@@ -17,7 +17,7 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "10.0.8")
|
.HasAnnotation("ProductVersion", "10.0.9")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
@@ -309,7 +309,6 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
|||||||
.HasDefaultValue(true);
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
b.Property<string>("Phone")
|
b.Property<string>("Phone")
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
@@ -321,7 +320,6 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
|||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Website")
|
b.Property<string>("Website")
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
using LiteCharms.Features.Api.Configuration;
|
||||||
using LiteCharms.Features.Api.Configuration;
|
|
||||||
using LiteCharms.Features.Api.Models;
|
using LiteCharms.Features.Api.Models;
|
||||||
using LiteCharms.Features.Api.Sdk;
|
using LiteCharms.Features.Api.Sdk;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Api;
|
namespace LiteCharms.Features.Api;
|
||||||
|
|
||||||
public sealed class TokenService(IConnectApi connectApi, IOptions<LiteCharmsClientSettings> clientOptions) : IService
|
public sealed class TokenService(IConnectApi connectApi, IOptions<LiteCharmsClientSettings> clientOptions)
|
||||||
{
|
{
|
||||||
private readonly LiteCharmsClientSettings clientSettings = clientOptions.Value;
|
private readonly LiteCharmsClientSettings clientSettings = clientOptions.Value;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
namespace LiteCharms.Features.Browser;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Browser;
|
public sealed class LocalStorageService(ProtectedLocalStorage storage)
|
||||||
|
|
||||||
public sealed class LocalStorageService(ProtectedLocalStorage storage) : IService
|
|
||||||
{
|
{
|
||||||
public async ValueTask<Result> DeleteAsync(string key)
|
public async ValueTask<Result> DeleteAsync(string key)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ public static class Api
|
|||||||
options.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
|
options.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.AddScoped<TokenService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user