Skip to content

SudoSOS Back-end API / stripe/payment-request-service / PaymentRequestService

Class: PaymentRequestService ​

Service layer for PaymentRequest.

Creation & validation ​

  • createPaymentRequest validates the beneficiary user and persists a fresh PENDING request with a fixed amount and expiry.

Lookup ​

  • getPaymentRequest(id) fetches a single request with its relations.
  • getPaymentRequests(filters, pagination) is the paginated admin listing.

Payment session bootstrap ​

Lives in PaymentRequestCheckoutService to keep this service free of any direct dependency on StripeService. That separation is what lets StripeService import PaymentRequestService (for the webhook settlement hook) without forming a cycle.

State transitions ​

  • cancelPaymentRequest moves PENDING → CANCELLED (rejects all other source states).
  • markFulfilledExternally is the admin escape hatch when a user paid out-of-band (e.g. bank transfer). Creates the void→user credit Transfer manually and marks the request PAID.
  • markPaid is called from the Stripe webhook when a linked payment intent reaches SUCCEEDED. Idempotent — already-PAID requests are left alone.

Extends ​

Constructors ​

Constructor ​

ts
new PaymentRequestService(manager?): PaymentRequestService;

Parameters ​

ParameterType
manager?EntityManager

Returns ​

PaymentRequestService

Overrides ​

WithManager.constructor

Properties ​

PropertyModifierTypeInherited from
managerprotectedEntityManagerWithManager.manager

Methods ​

cancelPaymentRequest() ​

ts
cancelPaymentRequest(request, cancelledBy): Promise<PaymentRequest>;

Cancel a PENDING request. Rejects any other source state (PAID, already CANCELLED, EXPIRED). Records cancelledAt and cancelledBy for audit.

Parameters ​

ParameterType
requestPaymentRequest
cancelledByUser

Returns ​

Promise<PaymentRequest>


configureLogger() ​

ts
protected configureLogger(logger): void;

Parameters ​

ParameterType
loggerLogger

Returns ​

void

Inherited from ​

WithManager.configureLogger


createPaymentRequest() ​

ts
createPaymentRequest(params): Promise<PaymentRequest>;

Create and persist a new PaymentRequest. Amount is immutable after this.

Parameters ​

ParameterType
paramsCreatePaymentRequestParams

Returns ​

Promise<PaymentRequest>

Throws ​

if params.for is ineligible.

Throws ​

if params.expiresAt is not strictly in the future, if params.amount is not strictly positive, or if params.description exceeds PAYMENT_REQUEST_DESCRIPTION_MAX_LENGTH.


getPaymentRequest() ​

ts
getPaymentRequest(id): Promise<PaymentRequest>;

Fetch a single PaymentRequest by id, including its relations. Returns null when no request with that id exists.

Parameters ​

ParameterType
idstring

Returns ​

Promise<PaymentRequest>


getPaymentRequests() ​

ts
getPaymentRequests(filters?, pagination?): Promise<[PaymentRequest[], number]>;

Paginated filtered listing of PaymentRequests. Status is a derived getter (see PaymentRequest.status), but we translate each candidate status into an equivalent SQL predicate on the stored paidAt / cancelledAt / expiresAt columns so pagination happens in the database.

Status → predicate mapping (precedence: PAID > CANCELLED > EXPIRED > PENDING):

  • PAID → paidAt IS NOT NULL
  • CANCELLED → paidAt IS NULL AND cancelledAt IS NOT NULL
  • EXPIRED → paidAt IS NULL AND cancelledAt IS NULL AND expiresAt < NOW()
  • PENDING → paidAt IS NULL AND cancelledAt IS NULL AND expiresAt >= NOW()

Multiple statuses are OR-combined inside the brackets so the AND with the non-status predicates stays correct.

Parameters ​

ParameterType
filtersPaymentRequestFilterParameters
paginationPaginationParameters

Returns ​

Promise<[PaymentRequest[], number]>


getPublicPaymentRequest() ​

ts
getPublicPaymentRequest(id): Promise<PaymentRequest>;

Lighter-weight lookup for the public share-link surface: loads only the for relation (needed for forDisplayName) and skips the audit joins that the public response intentionally omits. Keeps the unauthenticated route lean since anyone holding a link can hit it.

Parameters ​

ParameterType
idstring

Returns ​

Promise<PaymentRequest>


markFulfilledExternally() ​

ts
markFulfilledExternally(
   request, 
   reason, 
actor): Promise<PaymentRequest>;

Admin escape hatch: the user paid out-of-band (e.g. bank transfer). Creates the void→user credit Transfer manually and flips the request to PAID. A reason is required for the audit description; the acting admin is persisted on the request as fulfilledBy for audit.

Rejects any non-PENDING source state.

Parameters ​

ParameterTypeDescription
requestPaymentRequestThe PENDING PaymentRequest to fulfill.
reasonstringNon-empty audit reason (included in the Transfer description).
actorUserThe admin performing the escape hatch. Persisted as fulfilledBy so the audit trail shows who flipped the request.

Returns ​

Promise<PaymentRequest>


markPaid() ​

ts
markPaid(request): Promise<PaymentRequest>;

Idempotent: already-PAID requests are left unchanged.

Does not create a credit Transfer itself — callers own that. For a StripeDeposit-backed intent, StripeService.handleStripeDepositPaid creates it; for a PaymentRequest- originated intent (which never gets a StripeDeposit row), settlePaidStripeIntent creates it before calling this. This method only records the paidAt timestamp.

Parameters ​

ParameterType
requestPaymentRequest

Returns ​

Promise<PaymentRequest>


settlePaidStripeIntent() ​

ts
settlePaidStripeIntent(paymentIntent): Promise<PaymentRequest>;

Called by the Stripe webhook ingestion path (stripe!StripeWebhookService.createNewPaymentIntentStatus) when a linked payment intent reaches SUCCEEDED. Returns null when no request is linked.

A PaymentRequest-originated payment intent never gets a StripeDeposit row (see PaymentRequestCheckoutService.startPayment), so unlike a deposit it has no other settlement path creating its credit Transfer. This method is that path: it creates the Transfer and then marks the request PAID.

The caller is expected to have loaded the paymentRequestAttempt relation (nested down to the PaymentRequest itself, for its eager for) on paymentIntent already.

Parameters ​

ParameterType
paymentIntentStripePaymentIntent

Returns ​

Promise<PaymentRequest>


asBasePaymentRequestResponse() ​

ts
static asBasePaymentRequestResponse(request): BasePaymentRequestResponse;

Convert a PaymentRequest entity into the standard authenticated response.

Parameters ​

ParameterType
requestPaymentRequest

Returns ​

BasePaymentRequestResponse


asPublicPaymentRequestResponse() ​

ts
static asPublicPaymentRequestResponse(request): PublicPaymentRequestResponse;

Convert a PaymentRequest entity into the trimmed unauthenticated response served from the public share-link surface.

Parameters ​

ParameterType
requestPaymentRequest

Returns ​

PublicPaymentRequestResponse


validatePayable() ​

ts
static validatePayable(user): void;

Validate a candidate beneficiary for a PaymentRequest.

Rules:

  • Soft-deleted users are always rejected.
  • User type must be on validPaymentRequestBeneficiaryTypes. INVOICE is intentionally on the allow list so alumni with an invoice account can pay off their balance via a shareable link.
  • Inactive users are allowed (the whole point is that they can still settle outstanding balances).

Parameters ​

ParameterType
userUser

Returns ​

void

Throws ​

when the user is ineligible.