Skip to main content

Payments Structure

The following object definitions are used to manage payments efficiently:

  • Payment Type
  • Payment Method
  • Partner

This structure provides a flexible and extensible way to manage payments, allowing for redundancy through multiple partners associated with each payment method. If one partner is unavailable, the next partner is automatically chosen based on a predefined priority order set in the PartnerPaymentMethod table's priority field. Mapping tables relate these objects, and the architecture diagram offers more information.

Key Concepts

There are two main types of payments:

  1. Synchronous Payments: Processed immediately, providing an instant response.
  2. Asynchronous Payments: Most payments fall under this category and are completed after some time or an offline process (e.g., QR code payments, UPI, card, direct debit). Asynchronous payments involve external processing and can take longer, with status updates being sent to the system through a webhook mechanism.

Relationships

These relationships are represented in the following diagram:

image

Payment Type

A payment type refers to the general form of a payment, like a bank transfer or QR code. Each payment type can have one or more payment methods.

The following payment types are available, with the flexibility to add more in the future:

  • Bank transfers (banktransfer)
  • Card (card)
  • Direct Debit (directdebit)
  • Outlet (outlet)
  • QR Code (qrcode)
  • E-Wallet (ewallet)

For more information, refer to the Payment Types documentation.

Payment Method

A payment method specifies the detailed way of processing a payment. For example, a bank transfer could use sg_dummy_banktransfer. Examples:

  • id_banktransfer_mandiri – Bank transfer via Mandiri Bank in Indonesia
  • id_outlet_indomaret – Payment via an outlet using Indomaret in Indonesia
  • sg_qr_paynow – Payment via QR code using PayNow in Singapore
  • id_card_all – Card payment in Indonesia

Refer to the Payment Methods documentation for details.

Channels in Payment Methods

In the payment system, each payment method may have multiple channels for users to complete transactions, if available. Channels are part of each payment method's configuration. They are read-only and designed for user guidance, indicating the options for executing payments under each method.

Examples of channels include:

  • Bank Transfer Options: Different banks or payment networks.
  • Outlet Options: Specific outlets, like convenience stores, that accept payments.
  • QR Code Options: Different QR code providers for digital payments.

For specific implementation details, refer to:

  • Channel Information Retrieval: Stored in each payment method configuration.
  • User Interface Display: Channels are shown to users on the hosted checkout page or through any payment initiation interface.

Partner

A partner is a third-party service used to process payments. Each payment method may be fulfilled by multiple partners (e.g., xendit, midtrans, doku, rapyd) to enable redundancy and optimize cost.

Routing and Selection

The system determines which partner to use based on factors like cost and availability. This selection, called routing, is defined in the database as an ordered list. Routing is currently managed manually, with partners prioritized as per set conditions in the PartnerPaymentMethod table.

PartnerPaymentMethod

A Partner Payment Method maps payment methods to partners. For example, sg_qr_paynow can be mapped to the partner xendit. This mapping allows payment redundancy and fallback in case one partner fails.

Refer to the Partner Payment Methods documentation for more information.

Payment

A payment transaction records the details of a payment. It includes a sequence of PartnerPaymentMethods used to process the payment.

Model Payment

model Payment {
id String @id @default(uuid())
organisation_id String
payment_method_code String
currency String
amount Float
is_fully_refunded Boolean @default(false)
inputs Json
outputs Json
status String
failure_code String?
created_at DateTime @default(now())
updated_at DateTime @default(now())
deleted_at DateTime?
PaymentMethod PaymentMethod @relation(fields: [payment_method_code], references: [code])
PartnersOnPayments PartnersOnPayments[]
Refunds Refund[]
LedgerTransaction LedgerTransaction[]
}

For additional details, refer to the Payments documentation.

Creating a Payment

There are three ways to initiate a payment:

  1. Hosted Checkout Page: The hosted page can start multiple payments for different methods. Payments are created in the initiated state; only one payment moves forward after a selection.
  2. API – Initiate Payment: Calling the initiate payment endpoint.
  3. API – Create Payment: Calling the create payment endpoint, which places the payment into the initiated state, then processes it to pending.

Updating a Payment

Since most payments are asynchronous, their status updates may arrive after processing via webhooks or polling. Currently, webhooks handle these updates.

Webhook Delivery

Partners send status updates as webhooks. For security, webhooks may use a VPN, nonce, and signature validation. The webhook mechanism includes duplicate prevention logic to avoid processing the same update twice.

Webhook Implementation

For each partner, a dedicated route handles the webhook. Example files for implementation:

Refer to the Partner Webhooks API documentation for detailed information.

Payment States

The following payment states indicate progression:

  • pending
  • completed
  • cancelled
  • failed
  • partial_refund
  • full_refund

Payment State Transitions

Payments transition from initiated to pending and may move to completed or failed. For asynchronous payments, status updates occur via webhooks.

Refunds

Refunds log transactions that have been refunded, using associated PartnerPaymentMethods. For further details, refer to the Refunds documentation.