Shop It Docs
Developer ResourcesPayment

Gateway Interface

Payment gateway contract used by order checkout and buy-now flows.

Gateway Interface

Audience: Backend developers implementing gateways Scope: Initiate, verify, redirect payload parsing, optional webhook support

Interface Contract

export type InitiationType = "form_post" | "redirect" | "sdk" | "cod";

export interface PaymentGateway {
  readonly name: string;
  initiate(params: InitiatePaymentParams): Promise<InitiatePaymentResult>;
  verify(params: VerifyPaymentParams): Promise<VerifyPaymentResult>;
  parseRedirectPayload?(rawPayload: Record<string, unknown>):
    | ParsedPaymentRedirectPayload
    | Promise<ParsedPaymentRedirectPayload>;
}

initiate()

PaymentService creates a pending payment, then calls gateway initiate().

export interface InitiatePaymentParams {
  amountNpr: number;
  referenceId: number;
  referenceType: string;
  userId: string;
  successUrl: string;
  failureUrl: string;
}

export interface InitiatePaymentResult {
  gatewayTransactionId: string;
  initiationType: InitiationType;
  redirectUrl: string;
  gatewayPayload: Record<string, string>;
}

verify()

Used during redirect reconciliation.

export interface VerifyPaymentParams {
  gatewayTransactionId: string;
  gatewayResponse: Record<string, unknown>;
}

export interface VerifyPaymentResult {
  success: boolean;
  amountNpr: number;
}

Stripe-Specific Extension

Stripe gateway adds a webhook signature helper for controller use:

constructWebhookEvent(rawBody: Buffer | string, signature: string): Stripe.Event

Webhook-first finalization is handled by PaymentService.processStripeWebhookEvent(...).

Frontend Rule

Frontend logic is always:

  1. call initiation endpoint once;
  2. perform redirect/form-post by initiationType;
  3. wait for result-page redirect.

No frontend manual verification call is required.

Pending-Collection Gateways (COD)

COD is a special gateway that does not require a redirect or webhook. The PaymentService detects initiationType: "cod" after gateway.initiate() returns and creates a pending collection reference without completing payment inline:

  1. CodGateway.initiate() returns initiationType: "cod", gatewayTransactionId: "", redirectUrl: "".
  2. PaymentService updates the payment record with gatewayTransactionId = "cod_{paymentId}" and keeps status "pending".
  3. PaymentService does not emit payment.completed for COD checkout.
  4. Order checkout confirms COD after reservation by setting orders.paymentStatus = "cod_pending" while keeping orders.orderStatus = "payment_pending".
  5. Admin cash collection completes the existing pending payment row and sets orders.paymentStatus = "cod_collected".
  6. Response to frontend: initiationType: "cod", redirectUrl: returnUrl (set by order-customer service, not gateway).

No parseRedirectPayload() is needed for COD. No redirect controller is involved.