mirror of
https://git.v0l.io/Kieran/void.cat.git
synced 2025-11-15 20:27:00 +01:00
Rename paywall to payment
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using VoidCat.Model;
|
using VoidCat.Model;
|
||||||
using VoidCat.Model.Paywall;
|
using VoidCat.Model.Payments;
|
||||||
using VoidCat.Services.Abstractions;
|
using VoidCat.Services.Abstractions;
|
||||||
using VoidCat.Services.Files;
|
using VoidCat.Services.Files;
|
||||||
|
|
||||||
@@ -12,16 +12,16 @@ public class DownloadController : Controller
|
|||||||
{
|
{
|
||||||
private readonly FileStoreFactory _storage;
|
private readonly FileStoreFactory _storage;
|
||||||
private readonly FileInfoManager _fileInfo;
|
private readonly FileInfoManager _fileInfo;
|
||||||
private readonly IPaywallOrderStore _paywallOrders;
|
private readonly IPaymentOrderStore _paymentOrders;
|
||||||
private readonly ILogger<DownloadController> _logger;
|
private readonly ILogger<DownloadController> _logger;
|
||||||
|
|
||||||
public DownloadController(FileStoreFactory storage, ILogger<DownloadController> logger, FileInfoManager fileInfo,
|
public DownloadController(FileStoreFactory storage, ILogger<DownloadController> logger, FileInfoManager fileInfo,
|
||||||
IPaywallOrderStore paywall)
|
IPaymentOrderStore paymentOrderStore)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_storage = storage;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileInfo = fileInfo;
|
_fileInfo = fileInfo;
|
||||||
_paywallOrders = paywall;
|
_paymentOrders = paymentOrderStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpOptions]
|
[HttpOptions]
|
||||||
@@ -45,7 +45,7 @@ public class DownloadController : Controller
|
|||||||
var voidFile = await SetupDownload(gid);
|
var voidFile = await SetupDownload(gid);
|
||||||
if (voidFile == default) return;
|
if (voidFile == default) return;
|
||||||
|
|
||||||
var egressReq = new EgressRequest(gid, GetRanges(Request, (long)voidFile!.Metadata!.Size));
|
var egressReq = new EgressRequest(gid, GetRanges(Request, (long) voidFile!.Metadata!.Size));
|
||||||
if (egressReq.Ranges.Count() > 1)
|
if (egressReq.Ranges.Count() > 1)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Multi-range request not supported!");
|
_logger.LogWarning("Multi-range request not supported!");
|
||||||
@@ -57,10 +57,10 @@ public class DownloadController : Controller
|
|||||||
}
|
}
|
||||||
else if (egressReq.Ranges.Count() == 1)
|
else if (egressReq.Ranges.Count() == 1)
|
||||||
{
|
{
|
||||||
Response.StatusCode = (int)HttpStatusCode.PartialContent;
|
Response.StatusCode = (int) HttpStatusCode.PartialContent;
|
||||||
if (egressReq.Ranges.Sum(a => a.Size) == 0)
|
if (egressReq.Ranges.Sum(a => a.Size) == 0)
|
||||||
{
|
{
|
||||||
Response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable;
|
Response.StatusCode = (int) HttpStatusCode.RequestedRangeNotSatisfiable;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ public class DownloadController : Controller
|
|||||||
var preResult = await _storage.StartEgress(egressReq);
|
var preResult = await _storage.StartEgress(egressReq);
|
||||||
if (preResult.Redirect != null)
|
if (preResult.Redirect != null)
|
||||||
{
|
{
|
||||||
Response.StatusCode = (int)HttpStatusCode.Redirect;
|
Response.StatusCode = (int) HttpStatusCode.Redirect;
|
||||||
Response.Headers.Location = preResult.Redirect.ToString();
|
Response.Headers.Location = preResult.Redirect.ToString();
|
||||||
Response.ContentLength = 0;
|
Response.ContentLength = 0;
|
||||||
return;
|
return;
|
||||||
@@ -99,13 +99,13 @@ public class DownloadController : Controller
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check paywall
|
// check payment order
|
||||||
if (meta.Paywall != default && meta.Paywall.Service != PaymentServices.None)
|
if (meta.Payment != default && meta.Payment.Service != PaymentServices.None)
|
||||||
{
|
{
|
||||||
var orderId = Request.Headers.GetHeader("V-OrderId") ?? Request.Query["orderId"];
|
var orderId = Request.Headers.GetHeader("V-OrderId") ?? Request.Query["orderId"];
|
||||||
if (!await IsOrderPaid(orderId))
|
if (!await IsOrderPaid(orderId))
|
||||||
{
|
{
|
||||||
Response.StatusCode = (int)HttpStatusCode.PaymentRequired;
|
Response.StatusCode = (int) HttpStatusCode.PaymentRequired;
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,8 +121,8 @@ public class DownloadController : Controller
|
|||||||
{
|
{
|
||||||
if (Guid.TryParse(orderId, out var oid))
|
if (Guid.TryParse(orderId, out var oid))
|
||||||
{
|
{
|
||||||
var order = await _paywallOrders.Get(oid);
|
var order = await _paymentOrders.Get(oid);
|
||||||
if (order?.Status == PaywallOrderStatus.Paid)
|
if (order?.Status == PaymentOrderStatus.Paid)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|||||||
using Microsoft.AspNetCore.StaticFiles;
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using VoidCat.Model;
|
using VoidCat.Model;
|
||||||
using VoidCat.Model.Paywall;
|
using VoidCat.Model.Payments;
|
||||||
using VoidCat.Services.Abstractions;
|
using VoidCat.Services.Abstractions;
|
||||||
using VoidCat.Services.Files;
|
using VoidCat.Services.Files;
|
||||||
|
|
||||||
@@ -15,22 +15,22 @@ namespace VoidCat.Controllers
|
|||||||
{
|
{
|
||||||
private readonly FileStoreFactory _storage;
|
private readonly FileStoreFactory _storage;
|
||||||
private readonly IFileMetadataStore _metadata;
|
private readonly IFileMetadataStore _metadata;
|
||||||
private readonly IPaywallStore _paywall;
|
private readonly IPaymentStore _payment;
|
||||||
private readonly IPaywallFactory _paywallFactory;
|
private readonly IPaymentFactory _paymentFactory;
|
||||||
private readonly FileInfoManager _fileInfo;
|
private readonly FileInfoManager _fileInfo;
|
||||||
private readonly IUserUploadsStore _userUploads;
|
private readonly IUserUploadsStore _userUploads;
|
||||||
private readonly IUserStore _userStore;
|
private readonly IUserStore _userStore;
|
||||||
private readonly ITimeSeriesStatsReporter _timeSeriesStats;
|
private readonly ITimeSeriesStatsReporter _timeSeriesStats;
|
||||||
private readonly VoidSettings _settings;
|
private readonly VoidSettings _settings;
|
||||||
|
|
||||||
public UploadController(FileStoreFactory storage, IFileMetadataStore metadata, IPaywallStore paywall,
|
public UploadController(FileStoreFactory storage, IFileMetadataStore metadata, IPaymentStore payment,
|
||||||
IPaywallFactory paywallFactory, FileInfoManager fileInfo, IUserUploadsStore userUploads,
|
IPaymentFactory paymentFactory, FileInfoManager fileInfo, IUserUploadsStore userUploads,
|
||||||
ITimeSeriesStatsReporter timeSeriesStats, IUserStore userStore, VoidSettings settings)
|
ITimeSeriesStatsReporter timeSeriesStats, IUserStore userStore, VoidSettings settings)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_storage = storage;
|
||||||
_metadata = metadata;
|
_metadata = metadata;
|
||||||
_paywall = paywall;
|
_payment = payment;
|
||||||
_paywallFactory = paywallFactory;
|
_paymentFactory = paymentFactory;
|
||||||
_fileInfo = fileInfo;
|
_fileInfo = fileInfo;
|
||||||
_userUploads = userUploads;
|
_userUploads = userUploads;
|
||||||
_timeSeriesStats = timeSeriesStats;
|
_timeSeriesStats = timeSeriesStats;
|
||||||
@@ -204,20 +204,20 @@ namespace VoidCat.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a paywall order to pay
|
/// Create a payment order to pay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">File id</param>
|
/// <param name="id">File id</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("{id}/paywall")]
|
[Route("{id}/payment")]
|
||||||
public async ValueTask<PaywallOrder?> CreateOrder([FromRoute] string id)
|
public async ValueTask<PaymentOrder?> CreateOrder([FromRoute] string id)
|
||||||
{
|
{
|
||||||
var gid = id.FromBase58Guid();
|
var gid = id.FromBase58Guid();
|
||||||
var file = await _fileInfo.Get(gid);
|
var file = await _fileInfo.Get(gid);
|
||||||
var config = await _paywall.Get(gid);
|
var config = await _payment.Get(gid);
|
||||||
|
|
||||||
var provider = await _paywallFactory.CreateProvider(config!.Service);
|
var provider = await _paymentFactory.CreateProvider(config!.Service);
|
||||||
return await provider.CreateOrder(file!.Paywall!);
|
return await provider.CreateOrder(file!.Payment!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -227,25 +227,25 @@ namespace VoidCat.Controllers
|
|||||||
/// <param name="order">Order id</param>
|
/// <param name="order">Order id</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("{id}/paywall/{order:guid}")]
|
[Route("{id}/payment/{order:guid}")]
|
||||||
public async ValueTask<PaywallOrder?> GetOrderStatus([FromRoute] string id, [FromRoute] Guid order)
|
public async ValueTask<PaymentOrder?> GetOrderStatus([FromRoute] string id, [FromRoute] Guid order)
|
||||||
{
|
{
|
||||||
var gid = id.FromBase58Guid();
|
var gid = id.FromBase58Guid();
|
||||||
var config = await _paywall.Get(gid);
|
var config = await _payment.Get(gid);
|
||||||
|
|
||||||
var provider = await _paywallFactory.CreateProvider(config!.Service);
|
var provider = await _paymentFactory.CreateProvider(config!.Service);
|
||||||
return await provider.GetOrderStatus(order);
|
return await provider.GetOrderStatus(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the paywall config
|
/// Update the payment config
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">File id</param>
|
/// <param name="id">File id</param>
|
||||||
/// <param name="req">Requested config to set on the file</param>
|
/// <param name="req">Requested config to set on the file</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("{id}/paywall")]
|
[Route("{id}/payment")]
|
||||||
public async Task<IActionResult> SetPaywallConfig([FromRoute] string id, [FromBody] SetPaywallConfigRequest req)
|
public async Task<IActionResult> SetPaymentConfig([FromRoute] string id, [FromBody] SetPaymentConfigRequest req)
|
||||||
{
|
{
|
||||||
var gid = id.FromBase58Guid();
|
var gid = id.FromBase58Guid();
|
||||||
var meta = await _metadata.Get<SecretVoidFileMeta>(gid);
|
var meta = await _metadata.Get<SecretVoidFileMeta>(gid);
|
||||||
@@ -254,7 +254,7 @@ namespace VoidCat.Controllers
|
|||||||
|
|
||||||
if (req.Strike != default)
|
if (req.Strike != default)
|
||||||
{
|
{
|
||||||
await _paywall.Add(gid, new StrikePaywallConfig()
|
await _payment.Add(gid, new StrikePaymentConfig()
|
||||||
{
|
{
|
||||||
Service = PaymentServices.Strike,
|
Service = PaymentServices.Strike,
|
||||||
Handle = req.Strike.Handle,
|
Handle = req.Strike.Handle,
|
||||||
@@ -265,7 +265,7 @@ namespace VoidCat.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if none set, delete config
|
// if none set, delete config
|
||||||
await _paywall.Delete(gid);
|
await _payment.Delete(gid);
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,11 +335,11 @@ namespace VoidCat.Controllers
|
|||||||
=> new(false, null, message);
|
=> new(false, null, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record SetPaywallConfigRequest
|
public record SetPaymentConfigRequest
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(Base58GuidConverter))]
|
[JsonConverter(typeof(Base58GuidConverter))]
|
||||||
public Guid EditSecret { get; init; }
|
public Guid EditSecret { get; init; }
|
||||||
|
|
||||||
public StrikePaywallConfig? Strike { get; init; }
|
public StrikePaymentConfig? Strike { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
VoidCat/Model/Payments/PaymentMoney.cs
Normal file
22
VoidCat/Model/Payments/PaymentMoney.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace VoidCat.Model.Payments;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Money amount for payment orders
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Amount"></param>
|
||||||
|
/// <param name="Currency"></param>
|
||||||
|
public record PaymentMoney(decimal Amount, PaymentCurrencies Currency);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Supported payment currencies
|
||||||
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||||
|
public enum PaymentCurrencies : byte
|
||||||
|
{
|
||||||
|
BTC = 0,
|
||||||
|
USD = 1,
|
||||||
|
EUR = 2,
|
||||||
|
GBP = 3
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
namespace VoidCat.Model.Paywall;
|
namespace VoidCat.Model.Payments;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Status of paywall order
|
/// Status of payment order
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum PaywallOrderStatus : byte
|
public enum PaymentOrderStatus : byte
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoice is not paid yet
|
/// Invoice is not paid yet
|
||||||
@@ -22,9 +22,9 @@ public enum PaywallOrderStatus : byte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base paywall order
|
/// Base payment order
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PaywallOrder
|
public class PaymentOrder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique id of the order
|
/// Unique id of the order
|
||||||
@@ -44,18 +44,18 @@ public class PaywallOrder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The price of the order
|
/// The price of the order
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PaywallMoney Price { get; init; } = null!;
|
public PaymentMoney Price { get; init; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current status of the order
|
/// Current status of the order
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PaywallOrderStatus Status { get; set; }
|
public PaymentOrderStatus Status { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A paywall order lightning network invoice
|
/// A payment order lightning network invoice
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LightningPaywallOrder : PaywallOrder
|
public class LightningPaymentOrder : PaymentOrder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lightning invoice
|
/// Lightning invoice
|
||||||
17
VoidCat/Model/Payments/PaymentServices.cs
Normal file
17
VoidCat/Model/Payments/PaymentServices.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace VoidCat.Model.Payments;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Payment services supported by the system
|
||||||
|
/// </summary>
|
||||||
|
public enum PaymentServices
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No service
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Strike.me payment service
|
||||||
|
/// </summary>
|
||||||
|
Strike
|
||||||
|
}
|
||||||
45
VoidCat/Model/Payments/PaymentsConfig.cs
Normal file
45
VoidCat/Model/Payments/PaymentsConfig.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
namespace VoidCat.Model.Payments;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base payment config
|
||||||
|
/// </summary>
|
||||||
|
public abstract class PaymentConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// File this config is for
|
||||||
|
/// </summary>
|
||||||
|
public Guid File { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service used to pay the payment
|
||||||
|
/// </summary>
|
||||||
|
public PaymentServices Service { get; init; } = PaymentServices.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The cost for the payment to pass
|
||||||
|
/// </summary>
|
||||||
|
public PaymentMoney Cost { get; init; } = new(0m, PaymentCurrencies.BTC);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the payment is required
|
||||||
|
/// </summary>
|
||||||
|
public bool Required { get; init; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed class NoPaymentConfig : PaymentConfig
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Payment config for <see cref="PaymentServices.Strike"/> service
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Cost"></param>
|
||||||
|
public sealed class StrikePaymentConfig : PaymentConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Strike username to pay to
|
||||||
|
/// </summary>
|
||||||
|
public string Handle { get; init; } = null!;
|
||||||
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
namespace VoidCat.Model.Paywall;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Payment services supported by the system
|
|
||||||
/// </summary>
|
|
||||||
public enum PaymentServices
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// No service
|
|
||||||
/// </summary>
|
|
||||||
None,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Strike.me payment service
|
|
||||||
/// </summary>
|
|
||||||
Strike
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Base paywall config
|
|
||||||
/// </summary>
|
|
||||||
public abstract class PaywallConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// File this config is for
|
|
||||||
/// </summary>
|
|
||||||
public Guid File { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Service used to pay the paywall
|
|
||||||
/// </summary>
|
|
||||||
public PaymentServices Service { get; init; } = PaymentServices.None;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The cost for the paywall to pass
|
|
||||||
/// </summary>
|
|
||||||
public PaywallMoney Cost { get; init; } = new(0m, PaywallCurrencies.BTC);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public sealed class NoPaywallConfig : PaywallConfig
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Paywall config for <see cref="PaymentServices.Strike"/> service
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Cost"></param>
|
|
||||||
public sealed class StrikePaywallConfig : PaywallConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Strike username to pay to
|
|
||||||
/// </summary>
|
|
||||||
public string Handle { get; init; } = null!;
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace VoidCat.Model.Paywall;
|
|
||||||
|
|
||||||
public record PaywallMoney(decimal Amount, PaywallCurrencies Currency);
|
|
||||||
|
|
||||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
||||||
public enum PaywallCurrencies : byte
|
|
||||||
{
|
|
||||||
BTC = 0,
|
|
||||||
USD = 1,
|
|
||||||
EUR = 2,
|
|
||||||
GBP = 3
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using VoidCat.Model.Paywall;
|
using VoidCat.Model.Payments;
|
||||||
|
|
||||||
namespace VoidCat.Model
|
namespace VoidCat.Model
|
||||||
{
|
{
|
||||||
@@ -17,9 +17,9 @@ namespace VoidCat.Model
|
|||||||
public TMeta? Metadata { get; init; }
|
public TMeta? Metadata { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optional paywall config
|
/// Optional payment config
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PaywallConfig? Paywall { get; init; }
|
public PaymentConfig? Payment { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User profile that uploaded the file
|
/// User profile that uploaded the file
|
||||||
|
|||||||
16
VoidCat/Services/Abstractions/IPaymentFactory.cs
Normal file
16
VoidCat/Services/Abstractions/IPaymentFactory.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using VoidCat.Model.Payments;
|
||||||
|
|
||||||
|
namespace VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory class to access service provider implementations
|
||||||
|
/// </summary>
|
||||||
|
public interface IPaymentFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create provider handler for specified service type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="svc"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
ValueTask<IPaymentProvider> CreateProvider(PaymentServices svc);
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using VoidCat.Model.Paywall;
|
using VoidCat.Model.Payments;
|
||||||
|
|
||||||
namespace VoidCat.Services.Abstractions;
|
namespace VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Paywall order store
|
/// Payment order store
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IPaywallOrderStore : IBasicStore<PaywallOrder>
|
public interface IPaymentOrderStore : IBasicStore<PaymentOrder>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the status of an order
|
/// Update the status of an order
|
||||||
@@ -13,5 +13,5 @@ public interface IPaywallOrderStore : IBasicStore<PaywallOrder>
|
|||||||
/// <param name="order"></param>
|
/// <param name="order"></param>
|
||||||
/// <param name="status"></param>
|
/// <param name="status"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ValueTask UpdateStatus(Guid order, PaywallOrderStatus status);
|
ValueTask UpdateStatus(Guid order, PaymentOrderStatus status);
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
using VoidCat.Model.Paywall;
|
using VoidCat.Model.Payments;
|
||||||
|
|
||||||
namespace VoidCat.Services.Abstractions;
|
namespace VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provider to generate orders for a specific config
|
/// Provider to generate orders for a specific config
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IPaywallProvider
|
public interface IPaymentProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create an order with the provider
|
/// Create an order with the provider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="file"></param>
|
/// <param name="file"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ValueTask<PaywallOrder?> CreateOrder(PaywallConfig file);
|
ValueTask<PaymentOrder?> CreateOrder(PaymentConfig file);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the status of an existing order with the provider
|
/// Get the status of an existing order with the provider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id"></param>
|
/// <param name="id"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ValueTask<PaywallOrder?> GetOrderStatus(Guid id);
|
ValueTask<PaymentOrder?> GetOrderStatus(Guid id);
|
||||||
}
|
}
|
||||||
10
VoidCat/Services/Abstractions/IPaymentStore.cs
Normal file
10
VoidCat/Services/Abstractions/IPaymentStore.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using VoidCat.Model.Payments;
|
||||||
|
|
||||||
|
namespace VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Store for payment configs
|
||||||
|
/// </summary>
|
||||||
|
public interface IPaymentStore : IBasicStore<PaymentConfig>
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using VoidCat.Model.Paywall;
|
|
||||||
|
|
||||||
namespace VoidCat.Services.Abstractions;
|
|
||||||
|
|
||||||
public interface IPaywallFactory
|
|
||||||
{
|
|
||||||
ValueTask<IPaywallProvider> CreateProvider(PaymentServices svc);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using VoidCat.Model.Paywall;
|
|
||||||
|
|
||||||
namespace VoidCat.Services.Abstractions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Store for paywall configs
|
|
||||||
/// </summary>
|
|
||||||
public interface IPaywallStore : IBasicStore<PaywallConfig>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -10,17 +10,17 @@ namespace VoidCat.Services.Files;
|
|||||||
public sealed class FileInfoManager
|
public sealed class FileInfoManager
|
||||||
{
|
{
|
||||||
private readonly IFileMetadataStore _metadataStore;
|
private readonly IFileMetadataStore _metadataStore;
|
||||||
private readonly IPaywallStore _paywallStore;
|
private readonly IPaymentStore _paymentStore;
|
||||||
private readonly IStatsReporter _statsReporter;
|
private readonly IStatsReporter _statsReporter;
|
||||||
private readonly IUserStore _userStore;
|
private readonly IUserStore _userStore;
|
||||||
private readonly IVirusScanStore _virusScanStore;
|
private readonly IVirusScanStore _virusScanStore;
|
||||||
private readonly IUserUploadsStore _userUploadsStore;
|
private readonly IUserUploadsStore _userUploadsStore;
|
||||||
|
|
||||||
public FileInfoManager(IFileMetadataStore metadataStore, IPaywallStore paywallStore, IStatsReporter statsReporter,
|
public FileInfoManager(IFileMetadataStore metadataStore, IPaymentStore paymentStore, IStatsReporter statsReporter,
|
||||||
IUserStore userStore, IVirusScanStore virusScanStore, IUserUploadsStore userUploadsStore)
|
IUserStore userStore, IVirusScanStore virusScanStore, IUserUploadsStore userUploadsStore)
|
||||||
{
|
{
|
||||||
_metadataStore = metadataStore;
|
_metadataStore = metadataStore;
|
||||||
_paywallStore = paywallStore;
|
_paymentStore = paymentStore;
|
||||||
_statsReporter = statsReporter;
|
_statsReporter = statsReporter;
|
||||||
_userStore = userStore;
|
_userStore = userStore;
|
||||||
_virusScanStore = virusScanStore;
|
_virusScanStore = virusScanStore;
|
||||||
@@ -75,7 +75,7 @@ public sealed class FileInfoManager
|
|||||||
public async ValueTask Delete(Guid id)
|
public async ValueTask Delete(Guid id)
|
||||||
{
|
{
|
||||||
await _metadataStore.Delete(id);
|
await _metadataStore.Delete(id);
|
||||||
await _paywallStore.Delete(id);
|
await _paymentStore.Delete(id);
|
||||||
await _statsReporter.Delete(id);
|
await _statsReporter.Delete(id);
|
||||||
await _virusScanStore.Delete(id);
|
await _virusScanStore.Delete(id);
|
||||||
}
|
}
|
||||||
@@ -84,11 +84,11 @@ public sealed class FileInfoManager
|
|||||||
where TMeta : VoidFileMeta where TFile : VoidFile<TMeta>, new()
|
where TMeta : VoidFileMeta where TFile : VoidFile<TMeta>, new()
|
||||||
{
|
{
|
||||||
var meta = _metadataStore.Get<TMeta>(id);
|
var meta = _metadataStore.Get<TMeta>(id);
|
||||||
var paywall = _paywallStore.Get(id);
|
var payment = _paymentStore.Get(id);
|
||||||
var bandwidth = _statsReporter.GetBandwidth(id);
|
var bandwidth = _statsReporter.GetBandwidth(id);
|
||||||
var virusScan = _virusScanStore.GetByFile(id);
|
var virusScan = _virusScanStore.GetByFile(id);
|
||||||
var uploader = _userUploadsStore.Uploader(id);
|
var uploader = _userUploadsStore.Uploader(id);
|
||||||
await Task.WhenAll(meta.AsTask(), paywall.AsTask(), bandwidth.AsTask(), virusScan.AsTask(), uploader.AsTask());
|
await Task.WhenAll(meta.AsTask(), payment.AsTask(), bandwidth.AsTask(), virusScan.AsTask(), uploader.AsTask());
|
||||||
|
|
||||||
if (meta.Result == default) return default;
|
if (meta.Result == default) return default;
|
||||||
var user = uploader.Result.HasValue ? await _userStore.Get<PublicVoidUser>(uploader.Result.Value) : null;
|
var user = uploader.Result.HasValue ? await _userStore.Get<PublicVoidUser>(uploader.Result.Value) : null;
|
||||||
@@ -97,7 +97,7 @@ public sealed class FileInfoManager
|
|||||||
{
|
{
|
||||||
Id = id,
|
Id = id,
|
||||||
Metadata = meta.Result,
|
Metadata = meta.Result,
|
||||||
Paywall = paywall.Result,
|
Payment = payment.Result,
|
||||||
Bandwidth = bandwidth.Result,
|
Bandwidth = bandwidth.Result,
|
||||||
Uploader = user?.Flags.HasFlag(VoidUserFlags.PublicProfile) == true ? user : null,
|
Uploader = user?.Flags.HasFlag(VoidUserFlags.PublicProfile) == true ? user : null,
|
||||||
VirusScan = virusScan.Result
|
VirusScan = virusScan.Result
|
||||||
|
|||||||
27
VoidCat/Services/Migrations/Database/03-PaywallToPayments.cs
Normal file
27
VoidCat/Services/Migrations/Database/03-PaywallToPayments.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using FluentMigrator;
|
||||||
|
|
||||||
|
namespace VoidCat.Services.Migrations.Database;
|
||||||
|
|
||||||
|
[Migration(20220908_1527)]
|
||||||
|
public class PaywallToPayments : Migration
|
||||||
|
{
|
||||||
|
public override void Up()
|
||||||
|
{
|
||||||
|
Rename.Table("Paywall")
|
||||||
|
.To("Payment");
|
||||||
|
|
||||||
|
Rename.Table("PaywallOrder")
|
||||||
|
.To("PaymentOrder");
|
||||||
|
|
||||||
|
Rename.Table("PaywallStrike")
|
||||||
|
.To("PaymentStrike");
|
||||||
|
|
||||||
|
Rename.Table("PaywallOrderLightning")
|
||||||
|
.To("PaymentOrderLightning");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Down()
|
||||||
|
{
|
||||||
|
// yolo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
|
|||||||
using VoidCat.Model;
|
using VoidCat.Model;
|
||||||
using VoidCat.Services.Abstractions;
|
using VoidCat.Services.Abstractions;
|
||||||
using VoidCat.Services.Files;
|
using VoidCat.Services.Files;
|
||||||
using VoidCat.Services.Paywall;
|
using VoidCat.Services.Payment;
|
||||||
using VoidCat.Services.Users;
|
using VoidCat.Services.Users;
|
||||||
|
|
||||||
namespace VoidCat.Services.Migrations;
|
namespace VoidCat.Services.Migrations;
|
||||||
@@ -15,20 +15,20 @@ public class MigrateToPostgres : IMigration
|
|||||||
private readonly VoidSettings _settings;
|
private readonly VoidSettings _settings;
|
||||||
private readonly IFileMetadataStore _fileMetadata;
|
private readonly IFileMetadataStore _fileMetadata;
|
||||||
private readonly ICache _cache;
|
private readonly ICache _cache;
|
||||||
private readonly IPaywallStore _paywallStore;
|
private readonly IPaymentStore _paymentStore;
|
||||||
private readonly IUserStore _userStore;
|
private readonly IUserStore _userStore;
|
||||||
private readonly IUserUploadsStore _userUploads;
|
private readonly IUserUploadsStore _userUploads;
|
||||||
private readonly FileStoreFactory _fileStore;
|
private readonly FileStoreFactory _fileStore;
|
||||||
|
|
||||||
public MigrateToPostgres(VoidSettings settings, ILogger<MigrateToPostgres> logger, IFileMetadataStore fileMetadata,
|
public MigrateToPostgres(VoidSettings settings, ILogger<MigrateToPostgres> logger, IFileMetadataStore fileMetadata,
|
||||||
ICache cache, IPaywallStore paywallStore, IUserStore userStore, IUserUploadsStore userUploads,
|
ICache cache, IPaymentStore paymentStore, IUserStore userStore, IUserUploadsStore userUploads,
|
||||||
FileStoreFactory fileStore)
|
FileStoreFactory fileStore)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
_fileMetadata = fileMetadata;
|
_fileMetadata = fileMetadata;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_paywallStore = paywallStore;
|
_paymentStore = paymentStore;
|
||||||
_userStore = userStore;
|
_userStore = userStore;
|
||||||
_userUploads = userUploads;
|
_userUploads = userUploads;
|
||||||
_fileStore = fileStore;
|
_fileStore = fileStore;
|
||||||
@@ -99,7 +99,7 @@ public class MigrateToPostgres : IMigration
|
|||||||
|
|
||||||
private async Task MigratePaywall()
|
private async Task MigratePaywall()
|
||||||
{
|
{
|
||||||
var cachePaywallStore = new CachePaywallStore(_cache);
|
var cachePaywallStore = new CachePaymentStore(_cache);
|
||||||
|
|
||||||
var files = await _fileMetadata.ListFiles<VoidFileMeta>(new(0, int.MaxValue));
|
var files = await _fileMetadata.ListFiles<VoidFileMeta>(new(0, int.MaxValue));
|
||||||
await foreach (var file in files.Results)
|
await foreach (var file in files.Results)
|
||||||
@@ -109,7 +109,7 @@ public class MigrateToPostgres : IMigration
|
|||||||
var old = await cachePaywallStore.Get(file.Id);
|
var old = await cachePaywallStore.Get(file.Id);
|
||||||
if (old != default)
|
if (old != default)
|
||||||
{
|
{
|
||||||
await _paywallStore.Add(file.Id, old);
|
await _paymentStore.Add(file.Id, old);
|
||||||
_logger.LogInformation("Migrated paywall config for {File}", file.Id);
|
_logger.LogInformation("Migrated paywall config for {File}", file.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
VoidCat/Services/Payment/CachePaymentOrderStore.cs
Normal file
26
VoidCat/Services/Payment/CachePaymentOrderStore.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using VoidCat.Model.Payments;
|
||||||
|
using VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
|
namespace VoidCat.Services.Payment;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IPaymentOrderStore"/>
|
||||||
|
public class CachePaymentOrderStore : BasicCacheStore<PaymentOrder>, IPaymentOrderStore
|
||||||
|
{
|
||||||
|
public CachePaymentOrderStore(ICache cache) : base(cache)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async ValueTask UpdateStatus(Guid order, PaymentOrderStatus status)
|
||||||
|
{
|
||||||
|
var old = await Get(order);
|
||||||
|
if (old == default) return;
|
||||||
|
|
||||||
|
old.Status = status;
|
||||||
|
|
||||||
|
await Add(order, old);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override string MapKey(Guid id) => $"payment:order:{id}";
|
||||||
|
}
|
||||||
28
VoidCat/Services/Payment/CachePaymentStore.cs
Normal file
28
VoidCat/Services/Payment/CachePaymentStore.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using VoidCat.Model.Payments;
|
||||||
|
using VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
|
namespace VoidCat.Services.Payment;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IPaymentStore"/>
|
||||||
|
public class CachePaymentStore : BasicCacheStore<PaymentConfig>, IPaymentStore
|
||||||
|
{
|
||||||
|
public CachePaymentStore(ICache database)
|
||||||
|
: base(database)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async ValueTask<PaymentConfig?> Get(Guid id)
|
||||||
|
{
|
||||||
|
var cfg = await Cache.Get<NoPaymentConfig>(MapKey(id));
|
||||||
|
return cfg?.Service switch
|
||||||
|
{
|
||||||
|
PaymentServices.None => cfg,
|
||||||
|
PaymentServices.Strike => await Cache.Get<StrikePaymentConfig>(MapKey(id)),
|
||||||
|
_ => default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override string MapKey(Guid id) => $"payment:config:{id}";
|
||||||
|
}
|
||||||
23
VoidCat/Services/Payment/PaymentFactory.cs
Normal file
23
VoidCat/Services/Payment/PaymentFactory.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using VoidCat.Model.Payments;
|
||||||
|
using VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
|
namespace VoidCat.Services.Payment;
|
||||||
|
|
||||||
|
public class PaymentFactory : IPaymentFactory
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services;
|
||||||
|
|
||||||
|
public PaymentFactory(IServiceProvider services)
|
||||||
|
{
|
||||||
|
_services = services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<IPaymentProvider> CreateProvider(PaymentServices svc)
|
||||||
|
{
|
||||||
|
return ValueTask.FromResult<IPaymentProvider>(svc switch
|
||||||
|
{
|
||||||
|
PaymentServices.Strike => _services.GetRequiredService<StrikePaymentProvider>(),
|
||||||
|
_ => throw new ArgumentException("Must have a payment config", nameof(svc))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
32
VoidCat/Services/Payment/PaymentStartup.cs
Normal file
32
VoidCat/Services/Payment/PaymentStartup.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using VoidCat.Model;
|
||||||
|
using VoidCat.Services.Abstractions;
|
||||||
|
using VoidCat.Services.Strike;
|
||||||
|
|
||||||
|
namespace VoidCat.Services.Payment;
|
||||||
|
|
||||||
|
public static class PaymentStartup
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add services required to use payment functions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="settings"></param>
|
||||||
|
public static void AddPaymentServices(this IServiceCollection services, VoidSettings settings)
|
||||||
|
{
|
||||||
|
services.AddTransient<IPaymentFactory, PaymentFactory>();
|
||||||
|
if (settings.HasPostgres())
|
||||||
|
{
|
||||||
|
services.AddTransient<IPaymentStore, PostgresPaymentStore>();
|
||||||
|
services.AddTransient<IPaymentOrderStore, PostgresPaymentOrderStore>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
services.AddTransient<IPaymentStore, CachePaymentStore>();
|
||||||
|
services.AddTransient<IPaymentOrderStore, CachePaymentOrderStore>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// strike
|
||||||
|
services.AddTransient<StrikeApi>();
|
||||||
|
services.AddTransient<StrikePaymentProvider>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +1,33 @@
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
using VoidCat.Model.Paywall;
|
using VoidCat.Model.Payments;
|
||||||
using VoidCat.Services.Abstractions;
|
using VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
namespace VoidCat.Services.Paywall;
|
namespace VoidCat.Services.Payment;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class PostgresPaywallOrderStore : IPaywallOrderStore
|
public class PostgresPaymentOrderStore : IPaymentOrderStore
|
||||||
{
|
{
|
||||||
private readonly PostgresConnectionFactory _connection;
|
private readonly PostgresConnectionFactory _connection;
|
||||||
|
|
||||||
public PostgresPaywallOrderStore(PostgresConnectionFactory connection)
|
public PostgresPaymentOrderStore(PostgresConnectionFactory connection)
|
||||||
{
|
{
|
||||||
_connection = connection;
|
_connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async ValueTask<PaywallOrder?> Get(Guid id)
|
public async ValueTask<PaymentOrder?> Get(Guid id)
|
||||||
{
|
{
|
||||||
await using var conn = await _connection.Get();
|
await using var conn = await _connection.Get();
|
||||||
var order = await conn.QuerySingleOrDefaultAsync<DtoPaywallOrder>(
|
var order = await conn.QuerySingleOrDefaultAsync<DtoPaymentOrder>(
|
||||||
@"select * from ""PaywallOrder"" where ""Id"" = :id", new {id});
|
@"select * from ""PaymentOrder"" where ""Id"" = :id", new {id});
|
||||||
if (order.Service is PaymentServices.Strike)
|
if (order.Service is PaymentServices.Strike)
|
||||||
{
|
{
|
||||||
var lnDetails = await conn.QuerySingleAsync<LightningPaywallOrder>(
|
var lnDetails = await conn.QuerySingleAsync<LightningPaymentOrder>(
|
||||||
@"select * from ""PaywallOrderLightning"" where ""Order"" = :id", new
|
@"select * from ""PaymentOrderLightning"" where ""Order"" = :id", new
|
||||||
{
|
{
|
||||||
id = order.Id
|
id = order.Id
|
||||||
});
|
});
|
||||||
return new LightningPaywallOrder
|
return new LightningPaymentOrder
|
||||||
{
|
{
|
||||||
Id = order.Id,
|
Id = order.Id,
|
||||||
File = order.File,
|
File = order.File,
|
||||||
@@ -43,18 +43,18 @@ public class PostgresPaywallOrderStore : IPaywallOrderStore
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ValueTask<IReadOnlyList<PaywallOrder>> Get(Guid[] ids)
|
public ValueTask<IReadOnlyList<PaymentOrder>> Get(Guid[] ids)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async ValueTask Add(Guid id, PaywallOrder obj)
|
public async ValueTask Add(Guid id, PaymentOrder obj)
|
||||||
{
|
{
|
||||||
await using var conn = await _connection.Get();
|
await using var conn = await _connection.Get();
|
||||||
await using var txn = await conn.BeginTransactionAsync();
|
await using var txn = await conn.BeginTransactionAsync();
|
||||||
await conn.ExecuteAsync(
|
await conn.ExecuteAsync(
|
||||||
@"insert into ""PaywallOrder""(""Id"", ""File"", ""Service"", ""Currency"", ""Amount"", ""Status"")
|
@"insert into ""PaymentOrder""(""Id"", ""File"", ""Service"", ""Currency"", ""Amount"", ""Status"")
|
||||||
values(:id, :file, :service, :currency, :amt, :status)",
|
values(:id, :file, :service, :currency, :amt, :status)",
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
@@ -66,10 +66,10 @@ values(:id, :file, :service, :currency, :amt, :status)",
|
|||||||
status = (int) obj.Status
|
status = (int) obj.Status
|
||||||
});
|
});
|
||||||
|
|
||||||
if (obj is LightningPaywallOrder ln)
|
if (obj is LightningPaymentOrder ln)
|
||||||
{
|
{
|
||||||
await conn.ExecuteAsync(
|
await conn.ExecuteAsync(
|
||||||
@"insert into ""PaywallOrderLightning""(""Order"", ""Invoice"", ""Expire"") values(:order, :invoice, :expire)",
|
@"insert into ""PaymentOrderLightning""(""Order"", ""Invoice"", ""Expire"") values(:order, :invoice, :expire)",
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
order = id,
|
order = id,
|
||||||
@@ -85,20 +85,20 @@ values(:id, :file, :service, :currency, :amt, :status)",
|
|||||||
public async ValueTask Delete(Guid id)
|
public async ValueTask Delete(Guid id)
|
||||||
{
|
{
|
||||||
await using var conn = await _connection.Get();
|
await using var conn = await _connection.Get();
|
||||||
await conn.ExecuteAsync(@"delete from ""PaywallOrder"" where ""Id"" = :id", new {id});
|
await conn.ExecuteAsync(@"delete from ""PaymentOrder"" where ""Id"" = :id", new {id});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async ValueTask UpdateStatus(Guid order, PaywallOrderStatus status)
|
public async ValueTask UpdateStatus(Guid order, PaymentOrderStatus status)
|
||||||
{
|
{
|
||||||
await using var conn = await _connection.Get();
|
await using var conn = await _connection.Get();
|
||||||
await conn.ExecuteAsync(@"update ""PaywallOrder"" set ""Status"" = :status where ""Id"" = :id",
|
await conn.ExecuteAsync(@"update ""PaymentOrder"" set ""Status"" = :status where ""Id"" = :id",
|
||||||
new {id = order, status = (int) status});
|
new {id = order, status = (int) status});
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class DtoPaywallOrder : PaywallOrder
|
private sealed class DtoPaymentOrder : PaymentOrder
|
||||||
{
|
{
|
||||||
public PaywallCurrencies Currency { get; init; }
|
public PaymentCurrencies Currency { get; init; }
|
||||||
public decimal Amount { get; init; }
|
public decimal Amount { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
using VoidCat.Model.Paywall;
|
using VoidCat.Model.Payments;
|
||||||
using VoidCat.Services.Abstractions;
|
using VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
namespace VoidCat.Services.Paywall;
|
namespace VoidCat.Services.Payment;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public sealed class PostgresPaywallStore : IPaywallStore
|
public sealed class PostgresPaymentStore : IPaymentStore
|
||||||
{
|
{
|
||||||
private readonly PostgresConnectionFactory _connection;
|
private readonly PostgresConnectionFactory _connection;
|
||||||
|
|
||||||
public PostgresPaywallStore(PostgresConnectionFactory connection)
|
public PostgresPaymentStore(PostgresConnectionFactory connection)
|
||||||
{
|
{
|
||||||
_connection = connection;
|
_connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async ValueTask<PaywallConfig?> Get(Guid id)
|
public async ValueTask<PaymentConfig?> Get(Guid id)
|
||||||
{
|
{
|
||||||
await using var conn = await _connection.Get();
|
await using var conn = await _connection.Get();
|
||||||
var svc = await conn.QuerySingleOrDefaultAsync<DtoPaywallConfig>(
|
var svc = await conn.QuerySingleOrDefaultAsync<DtoPaymentConfig>(
|
||||||
@"select * from ""Paywall"" where ""File"" = :file", new {file = id});
|
@"select * from ""Payment"" where ""File"" = :file", new {file = id});
|
||||||
if (svc != default)
|
if (svc != default)
|
||||||
{
|
{
|
||||||
switch (svc.Service)
|
switch (svc.Service)
|
||||||
@@ -28,8 +28,8 @@ public sealed class PostgresPaywallStore : IPaywallStore
|
|||||||
{
|
{
|
||||||
var handle =
|
var handle =
|
||||||
await conn.ExecuteScalarAsync<string>(
|
await conn.ExecuteScalarAsync<string>(
|
||||||
@"select ""Handle"" from ""PaywallStrike"" where ""File"" = :file", new {file = id});
|
@"select ""Handle"" from ""PaymentStrike"" where ""File"" = :file", new {file = id});
|
||||||
return new StrikePaywallConfig
|
return new StrikePaymentConfig
|
||||||
{
|
{
|
||||||
Cost = new(svc.Amount, svc.Currency),
|
Cost = new(svc.Amount, svc.Currency),
|
||||||
File = svc.File,
|
File = svc.File,
|
||||||
@@ -44,18 +44,18 @@ public sealed class PostgresPaywallStore : IPaywallStore
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ValueTask<IReadOnlyList<PaywallConfig>> Get(Guid[] ids)
|
public ValueTask<IReadOnlyList<PaymentConfig>> Get(Guid[] ids)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async ValueTask Add(Guid id, PaywallConfig obj)
|
public async ValueTask Add(Guid id, PaymentConfig obj)
|
||||||
{
|
{
|
||||||
await using var conn = await _connection.Get();
|
await using var conn = await _connection.Get();
|
||||||
await using var txn = await conn.BeginTransactionAsync();
|
await using var txn = await conn.BeginTransactionAsync();
|
||||||
await conn.ExecuteAsync(
|
await conn.ExecuteAsync(
|
||||||
@"insert into ""Paywall""(""File"", ""Service"", ""Amount"", ""Currency"") values(:file, :service, :amount, :currency)
|
@"insert into ""Payment""(""File"", ""Service"", ""Amount"", ""Currency"") values(:file, :service, :amount, :currency)
|
||||||
on conflict(""File"") do update set ""Service"" = :service, ""Amount"" = :amount, ""Currency"" = :currency",
|
on conflict(""File"") do update set ""Service"" = :service, ""Amount"" = :amount, ""Currency"" = :currency",
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
@@ -65,9 +65,9 @@ on conflict(""File"") do update set ""Service"" = :service, ""Amount"" = :amount
|
|||||||
currency = obj.Cost.Currency
|
currency = obj.Cost.Currency
|
||||||
});
|
});
|
||||||
|
|
||||||
if (obj is StrikePaywallConfig sc)
|
if (obj is StrikePaymentConfig sc)
|
||||||
{
|
{
|
||||||
await conn.ExecuteAsync(@"insert into ""PaywallStrike""(""File"", ""Handle"") values(:file, :handle)
|
await conn.ExecuteAsync(@"insert into ""PaymentStrike""(""File"", ""Handle"") values(:file, :handle)
|
||||||
on conflict(""File"") do update set ""Handle"" = :handle", new
|
on conflict(""File"") do update set ""Handle"" = :handle", new
|
||||||
{
|
{
|
||||||
file = id,
|
file = id,
|
||||||
@@ -82,12 +82,12 @@ on conflict(""File"") do update set ""Handle"" = :handle", new
|
|||||||
public async ValueTask Delete(Guid id)
|
public async ValueTask Delete(Guid id)
|
||||||
{
|
{
|
||||||
await using var conn = await _connection.Get();
|
await using var conn = await _connection.Get();
|
||||||
await conn.ExecuteAsync(@"delete from ""Paywall"" where ""File"" = :file", new {file = id});
|
await conn.ExecuteAsync(@"delete from ""Payment"" where ""File"" = :file", new {file = id});
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class DtoPaywallConfig : PaywallConfig
|
private sealed class DtoPaymentConfig : PaymentConfig
|
||||||
{
|
{
|
||||||
public PaywallCurrencies Currency { get; init; }
|
public PaymentCurrencies Currency { get; init; }
|
||||||
public decimal Amount { get; init; }
|
public decimal Amount { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using VoidCat.Model;
|
using VoidCat.Model;
|
||||||
using VoidCat.Model.Paywall;
|
using VoidCat.Model.Payments;
|
||||||
using VoidCat.Services.Abstractions;
|
using VoidCat.Services.Abstractions;
|
||||||
using VoidCat.Services.Strike;
|
using VoidCat.Services.Strike;
|
||||||
|
|
||||||
namespace VoidCat.Services.Paywall;
|
namespace VoidCat.Services.Payment;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class StrikePaywallProvider : IPaywallProvider
|
public class StrikePaymentProvider : IPaymentProvider
|
||||||
{
|
{
|
||||||
private readonly ILogger<StrikePaywallProvider> _logger;
|
private readonly ILogger<StrikePaymentProvider> _logger;
|
||||||
private readonly StrikeApi _strike;
|
private readonly StrikeApi _strike;
|
||||||
private readonly IPaywallOrderStore _orderStore;
|
private readonly IPaymentOrderStore _orderStore;
|
||||||
|
|
||||||
public StrikePaywallProvider(ILogger<StrikePaywallProvider> logger, StrikeApi strike, IPaywallOrderStore orderStore)
|
public StrikePaymentProvider(ILogger<StrikePaymentProvider> logger, StrikeApi strike, IPaymentOrderStore orderStore)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_strike = strike;
|
_strike = strike;
|
||||||
@@ -21,9 +21,9 @@ public class StrikePaywallProvider : IPaywallProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async ValueTask<PaywallOrder?> CreateOrder(PaywallConfig config)
|
public async ValueTask<PaymentOrder?> CreateOrder(PaymentConfig config)
|
||||||
{
|
{
|
||||||
IsStrikePaywall(config, out var strikeConfig);
|
IsStrikePayment(config, out var strikeConfig);
|
||||||
_logger.LogInformation("Generating invoice for {Currency} {Amount}", config.Cost.Currency, config.Cost.Amount);
|
_logger.LogInformation("Generating invoice for {Currency} {Amount}", config.Cost.Currency, config.Cost.Amount);
|
||||||
|
|
||||||
var currency = MapCurrency(strikeConfig.Cost.Currency);
|
var currency = MapCurrency(strikeConfig.Cost.Currency);
|
||||||
@@ -57,13 +57,13 @@ public class StrikePaywallProvider : IPaywallProvider
|
|||||||
var quote = await _strike.GetInvoiceQuote(invoice.InvoiceId);
|
var quote = await _strike.GetInvoiceQuote(invoice.InvoiceId);
|
||||||
if (quote != default)
|
if (quote != default)
|
||||||
{
|
{
|
||||||
var order = new LightningPaywallOrder
|
var order = new LightningPaymentOrder
|
||||||
{
|
{
|
||||||
Id = invoice.InvoiceId,
|
Id = invoice.InvoiceId,
|
||||||
File = config.File,
|
File = config.File,
|
||||||
Service = PaymentServices.Strike,
|
Service = PaymentServices.Strike,
|
||||||
Price = config.Cost,
|
Price = config.Cost,
|
||||||
Status = PaywallOrderStatus.Unpaid,
|
Status = PaymentOrderStatus.Unpaid,
|
||||||
Invoice = quote.LnInvoice!,
|
Invoice = quote.LnInvoice!,
|
||||||
Expire = DateTime.SpecifyKind(quote.Expiration.DateTime, DateTimeKind.Utc)
|
Expire = DateTime.SpecifyKind(quote.Expiration.DateTime, DateTimeKind.Utc)
|
||||||
};
|
};
|
||||||
@@ -80,10 +80,10 @@ public class StrikePaywallProvider : IPaywallProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async ValueTask<PaywallOrder?> GetOrderStatus(Guid id)
|
public async ValueTask<PaymentOrder?> GetOrderStatus(Guid id)
|
||||||
{
|
{
|
||||||
var order = await _orderStore.Get(id);
|
var order = await _orderStore.Get(id);
|
||||||
if (order is {Status: PaywallOrderStatus.Paid or PaywallOrderStatus.Expired}) return order;
|
if (order is {Status: PaymentOrderStatus.Paid or PaymentOrderStatus.Expired}) return order;
|
||||||
|
|
||||||
var providerOrder = await _strike.GetInvoice(id);
|
var providerOrder = await _strike.GetInvoice(id);
|
||||||
if (providerOrder != default)
|
if (providerOrder != default)
|
||||||
@@ -104,44 +104,44 @@ public class StrikePaywallProvider : IPaywallProvider
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PaywallOrderStatus MapStatus(InvoiceState providerOrderState)
|
private PaymentOrderStatus MapStatus(InvoiceState providerOrderState)
|
||||||
=> providerOrderState switch
|
=> providerOrderState switch
|
||||||
{
|
{
|
||||||
InvoiceState.UNPAID => PaywallOrderStatus.Unpaid,
|
InvoiceState.UNPAID => PaymentOrderStatus.Unpaid,
|
||||||
InvoiceState.PENDING => PaywallOrderStatus.Unpaid,
|
InvoiceState.PENDING => PaymentOrderStatus.Unpaid,
|
||||||
InvoiceState.PAID => PaywallOrderStatus.Paid,
|
InvoiceState.PAID => PaymentOrderStatus.Paid,
|
||||||
InvoiceState.CANCELLED => PaywallOrderStatus.Expired,
|
InvoiceState.CANCELLED => PaymentOrderStatus.Expired,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(providerOrderState), providerOrderState, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(providerOrderState), providerOrderState, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Currencies MapCurrency(PaywallCurrencies c)
|
private static Currencies MapCurrency(PaymentCurrencies c)
|
||||||
=> c switch
|
=> c switch
|
||||||
{
|
{
|
||||||
PaywallCurrencies.BTC => Currencies.BTC,
|
PaymentCurrencies.BTC => Currencies.BTC,
|
||||||
PaywallCurrencies.USD => Currencies.USD,
|
PaymentCurrencies.USD => Currencies.USD,
|
||||||
PaywallCurrencies.EUR => Currencies.EUR,
|
PaymentCurrencies.EUR => Currencies.EUR,
|
||||||
PaywallCurrencies.GBP => Currencies.GBP,
|
PaymentCurrencies.GBP => Currencies.GBP,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(c), c, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(c), c, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
private static PaywallCurrencies MapCurrency(Currencies c)
|
private static PaymentCurrencies MapCurrency(Currencies c)
|
||||||
=> c switch
|
=> c switch
|
||||||
{
|
{
|
||||||
Currencies.BTC => PaywallCurrencies.BTC,
|
Currencies.BTC => PaymentCurrencies.BTC,
|
||||||
Currencies.USD => PaywallCurrencies.USD,
|
Currencies.USD => PaymentCurrencies.USD,
|
||||||
Currencies.USDT => PaywallCurrencies.USD,
|
Currencies.USDT => PaymentCurrencies.USD,
|
||||||
Currencies.EUR => PaywallCurrencies.EUR,
|
Currencies.EUR => PaymentCurrencies.EUR,
|
||||||
Currencies.GBP => PaywallCurrencies.GBP,
|
Currencies.GBP => PaymentCurrencies.GBP,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(c), c, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(c), c, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
private static void IsStrikePaywall(PaywallConfig? cfg, out StrikePaywallConfig strikeConfig)
|
private static void IsStrikePayment(PaymentConfig? cfg, out StrikePaymentConfig strikeConfig)
|
||||||
{
|
{
|
||||||
if (cfg?.Service != PaymentServices.Strike)
|
if (cfg?.Service != PaymentServices.Strike)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Must be strike paywall");
|
throw new ArgumentException("Must be strike Payment");
|
||||||
}
|
}
|
||||||
|
|
||||||
strikeConfig = (cfg as StrikePaywallConfig)!;
|
strikeConfig = (cfg as StrikePaymentConfig)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using VoidCat.Model.Paywall;
|
|
||||||
using VoidCat.Services.Abstractions;
|
|
||||||
|
|
||||||
namespace VoidCat.Services.Paywall;
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IPaywallOrderStore"/>
|
|
||||||
public class CachePaywallOrderStore : BasicCacheStore<PaywallOrder>, IPaywallOrderStore
|
|
||||||
{
|
|
||||||
public CachePaywallOrderStore(ICache cache) : base(cache)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async ValueTask UpdateStatus(Guid order, PaywallOrderStatus status)
|
|
||||||
{
|
|
||||||
var old = await Get(order);
|
|
||||||
if (old == default) return;
|
|
||||||
|
|
||||||
old.Status = status;
|
|
||||||
|
|
||||||
await Add(order, old);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override string MapKey(Guid id) => $"paywall:order:{id}";
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using VoidCat.Model.Paywall;
|
|
||||||
using VoidCat.Services.Abstractions;
|
|
||||||
|
|
||||||
namespace VoidCat.Services.Paywall;
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IPaywallStore"/>
|
|
||||||
public class CachePaywallStore : BasicCacheStore<PaywallConfig>, IPaywallStore
|
|
||||||
{
|
|
||||||
public CachePaywallStore(ICache database)
|
|
||||||
: base(database)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async ValueTask<PaywallConfig?> Get(Guid id)
|
|
||||||
{
|
|
||||||
var cfg = await Cache.Get<NoPaywallConfig>(MapKey(id));
|
|
||||||
return cfg?.Service switch
|
|
||||||
{
|
|
||||||
PaymentServices.None => cfg,
|
|
||||||
PaymentServices.Strike => await Cache.Get<StrikePaywallConfig>(MapKey(id)),
|
|
||||||
_ => default
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override string MapKey(Guid id) => $"paywall:config:{id}";
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using VoidCat.Model.Paywall;
|
|
||||||
using VoidCat.Services.Abstractions;
|
|
||||||
|
|
||||||
namespace VoidCat.Services.Paywall;
|
|
||||||
|
|
||||||
public class PaywallFactory : IPaywallFactory
|
|
||||||
{
|
|
||||||
private readonly IServiceProvider _services;
|
|
||||||
|
|
||||||
public PaywallFactory(IServiceProvider services)
|
|
||||||
{
|
|
||||||
_services = services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<IPaywallProvider> CreateProvider(PaymentServices svc)
|
|
||||||
{
|
|
||||||
return ValueTask.FromResult<IPaywallProvider>(svc switch
|
|
||||||
{
|
|
||||||
PaymentServices.Strike => _services.GetRequiredService<StrikePaywallProvider>(),
|
|
||||||
_ => throw new ArgumentException("Must have a paywall config", nameof(svc))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using VoidCat.Model;
|
|
||||||
using VoidCat.Services.Abstractions;
|
|
||||||
using VoidCat.Services.Strike;
|
|
||||||
|
|
||||||
namespace VoidCat.Services.Paywall;
|
|
||||||
|
|
||||||
public static class PaywallStartup
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Add services required to use paywall functions
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="services"></param>
|
|
||||||
/// <param name="settings"></param>
|
|
||||||
public static void AddPaywallServices(this IServiceCollection services, VoidSettings settings)
|
|
||||||
{
|
|
||||||
services.AddTransient<IPaywallFactory, PaywallFactory>();
|
|
||||||
if (settings.HasPostgres())
|
|
||||||
{
|
|
||||||
services.AddTransient<IPaywallStore, PostgresPaywallStore>();
|
|
||||||
services.AddTransient<IPaywallOrderStore, PostgresPaywallOrderStore>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
services.AddTransient<IPaywallStore, CachePaywallStore>();
|
|
||||||
services.AddTransient<IPaywallOrderStore, CachePaywallOrderStore>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// strike
|
|
||||||
services.AddTransient<StrikeApi>();
|
|
||||||
services.AddTransient<StrikePaywallProvider>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,7 @@ using VoidCat.Services.Captcha;
|
|||||||
using VoidCat.Services.Files;
|
using VoidCat.Services.Files;
|
||||||
using VoidCat.Services.InMemory;
|
using VoidCat.Services.InMemory;
|
||||||
using VoidCat.Services.Migrations;
|
using VoidCat.Services.Migrations;
|
||||||
using VoidCat.Services.Paywall;
|
using VoidCat.Services.Payment;
|
||||||
using VoidCat.Services.Redis;
|
using VoidCat.Services.Redis;
|
||||||
using VoidCat.Services.Stats;
|
using VoidCat.Services.Stats;
|
||||||
using VoidCat.Services.Users;
|
using VoidCat.Services.Users;
|
||||||
@@ -66,7 +66,7 @@ public static class VoidStartup
|
|||||||
{
|
{
|
||||||
services.AddStorage(voidSettings);
|
services.AddStorage(voidSettings);
|
||||||
services.AddMetrics(voidSettings);
|
services.AddMetrics(voidSettings);
|
||||||
services.AddPaywallServices(voidSettings);
|
services.AddPaymentServices(voidSettings);
|
||||||
services.AddUserServices(voidSettings);
|
services.AddUserServices(voidSettings);
|
||||||
services.AddVirusScanner(voidSettings);
|
services.AddVirusScanner(voidSettings);
|
||||||
services.AddCaptcha(voidSettings);
|
services.AddCaptcha(voidSettings);
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ export function useApi() {
|
|||||||
Api: {
|
Api: {
|
||||||
info: () => getJson("GET", "/info"),
|
info: () => getJson("GET", "/info"),
|
||||||
fileInfo: (id) => getJson("GET", `/upload/${id}`, undefined, auth),
|
fileInfo: (id) => getJson("GET", `/upload/${id}`, undefined, auth),
|
||||||
setPaywallConfig: (id, cfg) => getJson("POST", `/upload/${id}/paywall`, cfg, auth),
|
setPaymentConfig: (id, cfg) => getJson("POST", `/upload/${id}/payment`, cfg, auth),
|
||||||
createOrder: (id) => getJson("GET", `/upload/${id}/paywall`),
|
createOrder: (id) => getJson("GET", `/upload/${id}/payment`),
|
||||||
getOrder: (file, order) => getJson("GET", `/upload/${file}/paywall/${order}`),
|
getOrder: (file, order) => getJson("GET", `/upload/${file}/payment/${order}`),
|
||||||
login: (username, password, captcha) => getJson("POST", `/auth/login`, {username, password, captcha}),
|
login: (username, password, captcha) => getJson("POST", `/auth/login`, {username, password, captcha}),
|
||||||
register: (username, password, captcha) => getJson("POST", `/auth/register`, {username, password, captcha}),
|
register: (username, password, captcha) => getJson("POST", `/auth/register`, {username, password, captcha}),
|
||||||
getUser: (id) => getJson("GET", `/user/${id}`, undefined, auth),
|
getUser: (id) => getJson("GET", `/user/${id}`, undefined, auth),
|
||||||
|
|||||||
@@ -37,19 +37,19 @@ export const ZiB = Math.pow(1024, 7);
|
|||||||
*/
|
*/
|
||||||
export const YiB = Math.pow(1024, 8);
|
export const YiB = Math.pow(1024, 8);
|
||||||
|
|
||||||
export const PaywallCurrencies = {
|
export const PaymentCurrencies = {
|
||||||
BTC: 0,
|
BTC: 0,
|
||||||
USD: 1,
|
USD: 1,
|
||||||
EUR: 2,
|
EUR: 2,
|
||||||
GBP: 3
|
GBP: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PaywallServices = {
|
export const PaymentServices = {
|
||||||
None: 0,
|
None: 0,
|
||||||
Strike: 1
|
Strike: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PaywallOrderState = {
|
export const PaymentOrderState = {
|
||||||
Unpaid: 0,
|
Unpaid: 0,
|
||||||
Paid: 1,
|
Paid: 1,
|
||||||
Expired: 2
|
Expired: 2
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
|
|
||||||
import {StrikePaywallConfig} from "./StrikePaywallConfig";
|
import {StrikePaymentConfig} from "./StrikePaymentConfig";
|
||||||
import {NoPaywallConfig} from "./NoPaywallConfig";
|
import {NoPaymentConfig} from "./NoPaymentConfig";
|
||||||
import {useApi} from "./Api";
|
import {useApi} from "./Api";
|
||||||
import "./FileEdit.css";
|
import "./FileEdit.css";
|
||||||
import {useSelector} from "react-redux";
|
import {useSelector} from "react-redux";
|
||||||
@@ -13,7 +13,7 @@ export function FileEdit(props) {
|
|||||||
const file = props.file;
|
const file = props.file;
|
||||||
const meta = file.metadata;
|
const meta = file.metadata;
|
||||||
const profile = useSelector(state => state.login.profile);
|
const profile = useSelector(state => state.login.profile);
|
||||||
const [paywall, setPaywall] = useState(file.paywall?.service);
|
const [payment, setPayment] = useState(file.payment?.service);
|
||||||
const [name, setName] = useState(meta?.name);
|
const [name, setName] = useState(meta?.name);
|
||||||
const [description, setDescription] = useState(meta?.description);
|
const [description, setDescription] = useState(meta?.description);
|
||||||
const [expiry, setExpiry] = useState(meta?.expires === undefined || meta?.expires === null ? null : moment(meta?.expires).unix() * 1000);
|
const [expiry, setExpiry] = useState(meta?.expires === undefined || meta?.expires === null ? null : moment(meta?.expires).unix() * 1000);
|
||||||
@@ -26,7 +26,7 @@ export function FileEdit(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveConfig(cfg) {
|
async function saveConfig(cfg) {
|
||||||
let req = await Api.setPaywallConfig(file.id, cfg);
|
let req = await Api.setPaymentConfig(file.id, cfg);
|
||||||
return req.ok;
|
return req.ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,13 +40,13 @@ export function FileEdit(props) {
|
|||||||
await Api.updateMetadata(file.id, meta);
|
await Api.updateMetadata(file.id, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPaywallConfig() {
|
function renderPaymentConfig() {
|
||||||
switch (paywall) {
|
switch (payment) {
|
||||||
case 0: {
|
case 0: {
|
||||||
return <NoPaywallConfig privateFile={privateFile} onSaveConfig={saveConfig}/>;
|
return <NoPaymentConfig privateFile={privateFile} onSaveConfig={saveConfig}/>;
|
||||||
}
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
return <StrikePaywallConfig file={file} privateFile={privateFile} onSaveConfig={saveConfig}/>
|
return <StrikePaymentConfig file={file} privateFile={privateFile} onSaveConfig={saveConfig}/>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -81,13 +81,13 @@ export function FileEdit(props) {
|
|||||||
<VoidButton onClick={(e) => saveMeta()} options={{showSuccess: true}}>Save</VoidButton>
|
<VoidButton onClick={(e) => saveMeta()} options={{showSuccess: true}}>Save</VoidButton>
|
||||||
</div>
|
</div>
|
||||||
<div className="flx-1">
|
<div className="flx-1">
|
||||||
<h3>Paywall Config</h3>
|
<h3>Payment Config</h3>
|
||||||
Type:
|
Type:
|
||||||
<select onChange={(e) => setPaywall(parseInt(e.target.value))} value={paywall}>
|
<select onChange={(e) => setPayment(parseInt(e.target.value))} value={payment}>
|
||||||
<option value={0}>None</option>
|
<option value={0}>None</option>
|
||||||
<option value={1}>Strike</option>
|
<option value={1}>Strike</option>
|
||||||
</select>
|
</select>
|
||||||
{renderPaywallConfig()}
|
{renderPaymentConfig()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.paywall {
|
.payment {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border: 1px solid lightgreen;
|
border: 1px solid lightgreen;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import "./FilePaywall.css";
|
import "./FilePayment.css";
|
||||||
import {FormatCurrency} from "./Util";
|
import {FormatCurrency} from "./Util";
|
||||||
import {PaywallServices} from "./Const";
|
import {PaymentServices} from "./Const";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import {LightningPaywall} from "./LightningPaywall";
|
import {LightningPayment} from "./LightningPayment";
|
||||||
import {useApi} from "./Api";
|
import {useApi} from "./Api";
|
||||||
import {VoidButton} from "./VoidButton";
|
import {VoidButton} from "./VoidButton";
|
||||||
|
|
||||||
export function FilePaywall(props) {
|
export function FilePayment(props) {
|
||||||
const {Api} = useApi();
|
const {Api} = useApi();
|
||||||
const file = props.file;
|
const file = props.file;
|
||||||
const pw = file.paywall;
|
const pw = file.payment;
|
||||||
const paywallKey = `paywall-${file.id}`;
|
const paymentKey = `payment-${file.id}`;
|
||||||
const onPaid = props.onPaid;
|
const onPaid = props.onPaid;
|
||||||
|
|
||||||
const [order, setOrder] = useState();
|
const [order, setOrder] = useState();
|
||||||
@@ -27,7 +27,7 @@ export function FilePaywall(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handlePaid(order) {
|
function handlePaid(order) {
|
||||||
window.localStorage.setItem(paywallKey, JSON.stringify(order));
|
window.localStorage.setItem(paymentKey, JSON.stringify(order));
|
||||||
if (typeof onPaid === "function") {
|
if (typeof onPaid === "function") {
|
||||||
onPaid();
|
onPaid();
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ export function FilePaywall(props) {
|
|||||||
|
|
||||||
if (!order) {
|
if (!order) {
|
||||||
return (
|
return (
|
||||||
<div className="paywall">
|
<div className="payment">
|
||||||
<h3>
|
<h3>
|
||||||
You must pay {FormatCurrency(pw.cost.amount, pw.cost.currency)} to view this file.
|
You must pay {FormatCurrency(pw.cost.amount, pw.cost.currency)} to view this file.
|
||||||
</h3>
|
</h3>
|
||||||
@@ -44,8 +44,8 @@ export function FilePaywall(props) {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
switch (pw.service) {
|
switch (pw.service) {
|
||||||
case PaywallServices.Strike: {
|
case PaymentServices.Strike: {
|
||||||
return <LightningPaywall file={file} order={order} onReset={reset} onPaid={handlePaid}/>;
|
return <LightningPayment file={file} order={order} onReset={reset} onPaid={handlePaid}/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -4,7 +4,7 @@ import {TextPreview} from "./TextPreview";
|
|||||||
import FeatherIcon from "feather-icons-react";
|
import FeatherIcon from "feather-icons-react";
|
||||||
import "./FilePreview.css";
|
import "./FilePreview.css";
|
||||||
import {FileEdit} from "./FileEdit";
|
import {FileEdit} from "./FileEdit";
|
||||||
import {FilePaywall} from "./FilePaywall";
|
import {FilePayment} from "./FilePayment";
|
||||||
import {useApi} from "./Api";
|
import {useApi} from "./Api";
|
||||||
import {Helmet} from "react-helmet";
|
import {Helmet} from "react-helmet";
|
||||||
import {FormatBytes} from "./Util";
|
import {FormatBytes} from "./Util";
|
||||||
@@ -27,9 +27,9 @@ export function FilePreview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderTypes() {
|
function renderTypes() {
|
||||||
if (info.paywall && info.paywall.service !== 0) {
|
if (info.payment && info.payment.service !== 0) {
|
||||||
if (!order) {
|
if (!order) {
|
||||||
return <FilePaywall file={info} onPaid={loadInfo}/>;
|
return <FilePayment file={info} onPaid={loadInfo}/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ export function FilePreview() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (info) {
|
if (info) {
|
||||||
let fileLink = info.metadata?.url ?? `${ApiHost}/d/${info.id}`;
|
let fileLink = info.metadata?.url ?? `${ApiHost}/d/${info.id}`;
|
||||||
let order = window.localStorage.getItem(`paywall-${info.id}`);
|
let order = window.localStorage.getItem(`payment-${info.id}`);
|
||||||
if (order) {
|
if (order) {
|
||||||
let orderObj = JSON.parse(order);
|
let orderObj = JSON.parse(order);
|
||||||
setOrder(orderObj);
|
setOrder(orderObj);
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
import {useEffect} from "react";
|
import {useEffect} from "react";
|
||||||
|
|
||||||
import {Countdown} from "./Countdown";
|
import {Countdown} from "./Countdown";
|
||||||
import {PaywallOrderState} from "./Const";
|
import {PaymentOrderState} from "./Const";
|
||||||
import {useApi} from "./Api";
|
import {useApi} from "./Api";
|
||||||
|
|
||||||
export function LightningPaywall(props) {
|
export function LightningPayment(props) {
|
||||||
const {Api} = useApi();
|
const {Api} = useApi();
|
||||||
const file = props.file;
|
const file = props.file;
|
||||||
const order = props.order;
|
const order = props.order;
|
||||||
@@ -23,7 +23,7 @@ export function LightningPaywall(props) {
|
|||||||
if (req.ok) {
|
if (req.ok) {
|
||||||
let order = await req.json();
|
let order = await req.json();
|
||||||
|
|
||||||
if (order.status === PaywallOrderState.Paid && typeof onPaid === "function") {
|
if (order.status === PaymentOrderState.Paid && typeof onPaid === "function") {
|
||||||
onPaid(order);
|
onPaid(order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import {VoidButton} from "./VoidButton";
|
import {VoidButton} from "./VoidButton";
|
||||||
|
|
||||||
export function NoPaywallConfig(props) {
|
export function NoPaymentConfig(props) {
|
||||||
const [saveStatus, setSaveStatus] = useState();
|
const [saveStatus, setSaveStatus] = useState();
|
||||||
const privateFile = props.privateFile;
|
const privateFile = props.privateFile;
|
||||||
const onSaveConfig = props.onSaveConfig;
|
const onSaveConfig = props.onSaveConfig;
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import FeatherIcon from "feather-icons-react";
|
import FeatherIcon from "feather-icons-react";
|
||||||
import {PaywallCurrencies} from "./Const";
|
import {PaymentCurrencies} from "./Const";
|
||||||
import {VoidButton} from "./VoidButton";
|
import {VoidButton} from "./VoidButton";
|
||||||
|
|
||||||
export function StrikePaywallConfig(props) {
|
export function StrikePaymentConfig(props) {
|
||||||
const file = props.file;
|
const file = props.file;
|
||||||
const privateFile = props.privateFile;
|
const privateFile = props.privateFile;
|
||||||
const onSaveConfig = props.onSaveConfig;
|
const onSaveConfig = props.onSaveConfig;
|
||||||
const paywall = file.paywall;
|
const payment = file.payment;
|
||||||
const editSecret = privateFile.metadata.editSecret;
|
const editSecret = privateFile.metadata.editSecret;
|
||||||
|
|
||||||
const [username, setUsername] = useState(paywall?.handle ?? "hrf");
|
const [username, setUsername] = useState(payment?.handle ?? "hrf");
|
||||||
const [currency, setCurrency] = useState(paywall?.cost.currency ?? PaywallCurrencies.USD);
|
const [currency, setCurrency] = useState(payment?.cost.currency ?? PaymentCurrencies.USD);
|
||||||
const [price, setPrice] = useState(paywall?.cost.amount ?? 1);
|
const [price, setPrice] = useState(payment?.cost.amount ?? 1);
|
||||||
const [saveStatus, setSaveStatus] = useState();
|
const [saveStatus, setSaveStatus] = useState();
|
||||||
|
|
||||||
async function saveStrikeConfig(e) {
|
async function saveStrikeConfig(e) {
|
||||||
@@ -31,7 +31,7 @@ export function StrikePaywallConfig(props) {
|
|||||||
if (await onSaveConfig(cfg)) {
|
if (await onSaveConfig(cfg)) {
|
||||||
setSaveStatus(true);
|
setSaveStatus(true);
|
||||||
} else {
|
} else {
|
||||||
alert("Error settings paywall config!");
|
alert("Error settings payment config!");
|
||||||
setSaveStatus(false);
|
setSaveStatus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,10 +45,10 @@ export function StrikePaywallConfig(props) {
|
|||||||
<dt>Currency:</dt>
|
<dt>Currency:</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<select onChange={(e) => setCurrency(parseInt(e.target.value))} value={currency}>
|
<select onChange={(e) => setCurrency(parseInt(e.target.value))} value={currency}>
|
||||||
<option value={PaywallCurrencies.BTC}>BTC</option>
|
<option value={PaymentCurrencies.BTC}>BTC</option>
|
||||||
<option value={PaywallCurrencies.USD}>USD</option>
|
<option value={PaymentCurrencies.USD}>USD</option>
|
||||||
<option value={PaywallCurrencies.EUR}>EUR</option>
|
<option value={PaymentCurrencies.EUR}>EUR</option>
|
||||||
<option value={PaywallCurrencies.GBP}>GBP</option>
|
<option value={PaymentCurrencies.GBP}>GBP</option>
|
||||||
</select>
|
</select>
|
||||||
</dd>
|
</dd>
|
||||||
<dt>Price:</dt>
|
<dt>Price:</dt>
|
||||||
@@ -12,7 +12,7 @@ services:
|
|||||||
redis:
|
redis:
|
||||||
image: "redis:alpine"
|
image: "redis:alpine"
|
||||||
ports:
|
ports:
|
||||||
- "6379:6379"
|
- "6079:6379"
|
||||||
postgres:
|
postgres:
|
||||||
image: "postgres:14.1"
|
image: "postgres:14.1"
|
||||||
ports:
|
ports:
|
||||||
@@ -22,3 +22,5 @@ services:
|
|||||||
- "POSTGRES_HOST_AUTH_METHOD=trust"
|
- "POSTGRES_HOST_AUTH_METHOD=trust"
|
||||||
clamav:
|
clamav:
|
||||||
image: "clamav/clamav"
|
image: "clamav/clamav"
|
||||||
|
ports:
|
||||||
|
- "3320:3310"
|
||||||
Reference in New Issue
Block a user