GuestOps/src/GuestOps.Api/PaymentEndpoints.cs
2026-09-30 10:24:27 +01:00

33 lines
3.5 KiB
C#

using System.Security.Claims;
namespace GuestOps.Web;
public static class PaymentEndpoints
{
public static void Map(RouteGroupBuilder api,bool preview)
{
var group=api.MapGroup("/payments").RequireRateLimiting("pms").RequireAuthorization(Access.ProviderLookup);
group.MapGet("/status",(HttpContext c,IConfiguration config)=>{var p=preview?null:NmiProfile.Read(config,Session.Hotel(c));return Results.Ok(new{configured=p!=null,createsConfigured=p?.CreatesEnabled==true,sandbox=p?.BaseUrl=="https://sandbox.nmi.com",preview});});
group.MapGet("/requests",async(HttpContext c,IStore store)=>Results.Ok((await store.List<PaymentRequest>(Session.Hotel(c))).OrderByDescending(p=>p.UpdatedAt).Select(p=>p.View())));
group.MapPost("/requests",async(PaymentInput input,HttpContext c,PaymentWork work,IStore store)=>{
if(preview)return Results.BadRequest(new{error="Real payment creation is disabled in preview."});
var p=await work.Propose(Session.Hotel(c),c.User.FindFirstValue(ClaimTypes.NameIdentifier)!,input);await Session.Audit(store,c,"Prepared payment request for review");return Results.Ok(p.View());
}).RequireAuthorization(Access.ProviderProposal);
group.MapPost("/requests/{id}/create",async(string id,PaymentApproval input,HttpContext c,PaymentWork work,IStore store)=>{
var p=await store.Get<PaymentRequest>(Session.Hotel(c),id);if(p==null)return Results.NotFound();if(preview)return Results.BadRequest();
var result=await work.Create(p,input.Version,c.User.FindFirstValue(ClaimTypes.NameIdentifier)!,input.EmailAndAmountApproved,c.RequestAborted);await Session.Audit(store,c,"Payment creation result: "+result.State);return Results.Ok(result.View());
}).RequireAuthorization(Access.ExternalApproval);
group.MapPost("/requests/{id}/check",async(string id,VersionInput input,HttpContext c,PaymentWork work,IStore store)=>{
var p=await store.Get<PaymentRequest>(Session.Hotel(c),id);if(p==null)return Results.NotFound();if(preview)return Results.BadRequest();
var result=await work.Check(p,input.Version,c.RequestAborted);await Session.Audit(store,c,"Checked payment invoice: "+result.State);return Results.Ok(result.View());
}).RequireAuthorization(Access.ExternalApproval);
group.MapPost("/requests/{id}/cancel",async(string id,VersionInput input,HttpContext c,PaymentWork work,IStore store)=>{
var p=await store.Get<PaymentRequest>(Session.Hotel(c),id);if(p==null)return Results.NotFound();if(preview)return Results.BadRequest();
var result=await work.Cancel(p,input.Version);await Session.Audit(store,c,"Cancelled unsubmitted payment proposal");return Results.Ok(result.View());
}).RequireAuthorization(Access.ExternalApproval);
group.MapPut("/controls",async(PmsControlsInput input,HttpContext c,IStore store,IConfiguration config)=>{
if(input.Enabled&&(preview||NmiProfile.Read(config,Session.Hotel(c))?.CreatesEnabled!=true))return Results.BadRequest(new{error="Administrator payment enablement and sandbox acceptance are required first."});
var hotel=await store.Get<Hotel>(Session.Hotel(c),Session.Hotel(c));if(hotel==null)return Results.NotFound();hotel.PaymentsEnabled=input.Enabled;hotel.Version=input.Version+1;
if(!await store.Replace(hotel.HotelId,hotel.Id,input.Version,hotel))return Input.Conflict();await Session.Audit(store,c,"Updated payment creation control");return Results.Ok(hotel);
}).RequireAuthorization(Access.Integrations);
}
}