Payment Model
Business Purpose: Records the details of a single, complete payment transaction made by a student. This model acts as a log, capturing the total amount paid and all the identifiers returned by the external payment gateway.
Fields:
| Field Name | Type | Description |
|---|---|---|
institute | relation | Many-to-one relationship to the institute model. |
student | relation | Many-to-one relationship to the student model. |
paymentGatewayOrderId | shortText | Order ID from the Payment Gateway. |
paymentGatewayPaymentId | shortText | Payment ID from the Payment Gateway. |
paymentGatewayTransId | shortText | Transaction ID from the Payment Gateway. |
paymentGatewayInvoiceId | shortText | Invoice ID from the Payment Gateway. |
paymentGatewayEncodedId | shortText | Encoded ID from the Payment Gateway. |
paymentGatewayStatus | shortText | Status from the Payment Gateway. |
amount | decimal | The amount of the payment. |
isRefunded | boolean | A flag indicating if the payment has been refunded. |
paymentStatus | selectionStatic | The status of the payment (e.g., Pending, Succeeded, Failed). |
paymentCollectionItemDetails | relation | One-to-many relationship to paymentCollectionItemDetail. |
Key Fields Explained
paymentGateway...Fields: This group of fields is essential for traceability. They store all the unique IDs and status codes returned by the payment gateway (e.g., Stripe, PayPal) after a transaction attempt. This is critical for reconciling payments, handling disputes, and debugging transaction issues.paymentStatus(Static Selection): This is our internal status for the payment, which is updated based on the callback we receive from the payment gateway.
Status Flows Explained
In the School Fees Portal, payment status is tracked at two different levels to provide a comprehensive view of a transaction's lifecycle. This logic is derived from the backend implementation and reflects how payments are processed.
1. Transaction-Level Status (Payment model)
This is the status of an individual payment attempt, stored in the paymentStatus field of the Payment model itself. It represents the direct outcome of an interaction with the payment gateway.
The flow is as follows:
Pending: A payment record is created when a user initiates a transaction. This is the initial state while the payment is being processed by the gateway.Succeeded: The gateway has successfully processed the payment. This is a final state for a successful transaction.Failed: The gateway could not process the payment due to an error, insufficient funds, or cancellation by the user. This is a final state for a failed transaction.
A simple representation of the flow:
[Pending] --(Gateway Confirms Success)--> [Succeeded]
|
+--(Gateway Confirms Failure)--> [Failed]2. Bill-Level Status (PaymentCollectionItem model)
This is a higher-level, calculated status that reflects the overall state of a bill or fee item, which might be settled through one or more partial payments. This status is stored in the status field of the PaymentCollectionItem model.
The flow is as follows:
Pending: The bill has been issued, but no successful payments have been made yet.Partially Paid: At least one payment hasSucceeded, but the total paid amount is still less than the total amount due for the bill.Fully Paid: The sum of allSucceededpayments now equals the total amount due for the bill.Cancelled: The bill has been administratively voided and is no longer payable.
This status is automatically updated by the system whenever a linked Payment record's status changes. For example, when a Payment moves to Succeeded, the system recalculates the total paid amount for the corresponding PaymentCollectionItem and updates its status accordingly.
paymentCollectionItemDetails(Inverse Relation): This field represents the "one-to-many" side of the relationship, linking onePaymenttransaction to the multiple fee items it covers. You do not create this field directly. When you define themany-to-onerelationship from thePaymentCollectionItemDetailmodel back to thePaymentmodel, SolidX automatically adds thispaymentCollectionItemDetailsfield. For example, a single payment of $100 could be linked to two detail records: $70 for "Tuition Fee" and $30 for "Bus Fee".
Creation Steps
If you are following the manual "Guided Tour", follow these steps to create the Payment model in the App Builder.
- Navigate to Fees Portal > App Builder > Model and click Add.
- Fill in the primary details for the model:
- Singular Name:
payment - Plural Name:
payments - Display Name:
Payment
- Singular Name:
- Go to the Fields tab.
- Click Add Field and create each field exactly as defined in the table above.
- Click Save.
For the Fast Track: Model Metadata The JSON block below contains the complete metadata definition for the Payment model.
This definition is structured with top-level properties for the model itself (like
singularName,pluralName,tableName) and afieldsarray that defines every attribute you see in the table above.You can use this metadata as part of the "Fast Track" approach by including it in the main
fees-portal-metadata.jsonfile.
{
"singularName": "payment",
"pluralName": "payments",
"displayName": "Payment",
"description": "This table allows us to store payment records of a user",
"dataSource": "default",
"dataSourceType": "postgres",
"tableName": "fees_portal_payment",
"isChild": false,
"enableAuditTracking": true,
"enableSoftDelete": false,
"draftPublishWorkflow": false,
"internationalisation": false,
"fields": [
{
"name": "institute",
"displayName": "Institute",
"description": null,
"type": "relation",
"ormType": "integer",
"isSystem": false,
"relationType": "many-to-one",
"relationCoModelFieldName": null,
"relationCreateInverse": false,
"relationCoModelSingularName": "institute",
"relationCoModelColumnName": null,
"relationModelModuleName": "fees-portal",
"relationCascade": "cascade",
"required": true,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"relationJoinTableName": null,
"isRelationManyToManyOwner": null,
"relationFieldFixedFilter": "",
"enableAuditTracking": true
},
{
"name": "student",
"displayName": "Student",
"description": null,
"type": "relation",
"ormType": "integer",
"isSystem": false,
"relationType": "many-to-one",
"relationCoModelFieldName": "payments",
"relationCreateInverse": true,
"relationCoModelSingularName": "student",
"relationCoModelColumnName": null,
"relationModelModuleName": "fees-portal",
"relationCascade": "cascade",
"required": true,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"relationJoinTableName": null,
"isRelationManyToManyOwner": null,
"relationFieldFixedFilter": "",
"enableAuditTracking": true
},
{
"name": "paymentGatewayOrderId",
"displayName": "Payment Gateway Order Id",
"description": null,
"type": "shortText",
"ormType": "varchar",
"isSystem": false,
"defaultValue": null,
"min": null,
"max": null,
"required": true,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"isUserKey": false,
"enableAuditTracking": true
},
{
"name": "paymentGatewayPaymentId",
"displayName": "Payment Gateway Payment Id",
"description": null,
"type": "shortText",
"ormType": "varchar",
"isSystem": false,
"defaultValue": null,
"min": null,
"max": null,
"required": false,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"isUserKey": false,
"enableAuditTracking": true
},
{
"name": "paymentGatewayTransId",
"displayName": "Payment Gateway Trans Id",
"description": null,
"type": "shortText",
"ormType": "varchar",
"isSystem": false,
"defaultValue": null,
"min": null,
"max": null,
"required": false,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"isUserKey": false,
"enableAuditTracking": true
},
{
"name": "paymentGatewayInvoiceId",
"displayName": "Payment Gateway Invoice Id",
"description": null,
"type": "shortText",
"ormType": "varchar",
"isSystem": false,
"defaultValue": null,
"min": null,
"max": null,
"required": false,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"isUserKey": false,
"enableAuditTracking": true
},
{
"name": "paymentGatewayEncodedId",
"displayName": "Payment Gateway Encoded Id",
"description": null,
"type": "shortText",
"ormType": "varchar",
"isSystem": false,
"defaultValue": null,
"min": null,
"max": null,
"required": false,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"isUserKey": false,
"enableAuditTracking": true
},
{
"name": "paymentGatewayStatus",
"displayName": "Payment Gateway Status",
"description": null,
"type": "shortText",
"ormType": "varchar",
"isSystem": false,
"defaultValue": null,
"min": null,
"max": null,
"required": false,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"isUserKey": false,
"enableAuditTracking": true
},
{
"name": "amount",
"displayName": "Amount",
"description": null,
"type": "decimal",
"ormType": "decimal",
"isSystem": false,
"defaultValue": null,
"min": null,
"max": null,
"required": true,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"enableAuditTracking": true
},
{
"name": "isRefunded",
"displayName": "Is Refunded",
"description": null,
"type": "boolean",
"ormType": "boolean",
"isSystem": false,
"defaultValue": null,
"required": true,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"enableAuditTracking": true
},
{
"name": "paymentStatus",
"displayName": "Payment Status",
"description": null,
"type": "selectionStatic",
"ormType": "varchar",
"isSystem": false,
"defaultValue": "Pending",
"selectionStaticValues": [
"Pending:Pending",
"Succeeded:Succeeded",
"Failed:Failed"
],
"selectionValueType": "string",
"required": true,
"unique": false,
"index": false,
"private": false,
"encrypt": false,
"encryptionType": null,
"decryptWhen": null,
"columnName": null,
"enableAuditTracking": true,
"isMultiSelect": false
}
]
}