Skip to content

Instantly share code, notes, and snippets.

@abdurraufahmad
Forked from vladimirfomene/Report.md
Created October 18, 2019 10:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abdurraufahmad/3debae8c0d497edef83807d6927bb4cf to your computer and use it in GitHub Desktop.
Save abdurraufahmad/3debae8c0d497edef83807d6927bb4cf to your computer and use it in GitHub Desktop.
Payment Gateway Project Report

Payment Gateway for the Mifos Platform and Beyonic Payment Service

Project Goals

  • Implement the Mifos payment gateway service to support the following use cases:
    • Real time disbursement
    • Batch disbursement
    • Customer initiated payments
  • Develop a Payment Gateway portal for the payment gateway.
  • Develop a particular mobile money integration on the payment gateway to show it works.
  • Document the api for the payment gateway.

Work Done

  • Implemented a payment gateway which supports the following use cases:
    • Real time disbursement
    • Batch disbursement
    • Customer initiated payments
  • Developed a mobile money integration with Beyonic to test the payment gateway.

Work Left

  • Implementing the payment gateway portal which will be used by MFIs and MMPs.
  • Documentating the payment gateway's api to facilitate integration with other MMP apis.
  • Implementing test on the payment gateway and Beyonic Payment service.

Setup information for both the Payment gateway and Beyonic's payment service

  • Download MySql 5.6 and above.
  • Download Java(JDK) 1.8 and above.
  • Download an IDE that support gradle, most preferably Intellij and turn on annotation processing in your settings.
  • Clone the code from the above repository
  • Upload the project into your favorite IDE as a Gradle project.
  • Run gradle build and then run the application.

Database Setup

  • Open your mysql and create a database called mifos-payment-gateway
  • Go to application.properties file in the resource folder of our application. - Change the spring.datasource.username to your database username and the spring.datasource.password to your database password.

API documentation for Payment Gateway

  • Customer Initiated payment (Loan Repayment)
    Example Request:

    POST http://localhost:8080/inbound/requests?tenant=default
    Content-Type: application/json
    Request Body:
    {
        "id" : 1,
        "transactType" : "LOAN_REPAYMENT",
        "paymentMethod" : "mobile money",
        "paymentMethodType" : "beyonic",
        "mmpId" : 1,
        "mfiId" : 1,
        "sourceRef" : "+233267881050",
        "destinationRef" : "+80000000001",
        "fineractAccNo" : "000000039",
        "fineractClientId" : 1,
        "amount" : 10,
        "transactionReason" : "Test Beyonic",
        "externalSystId" : 0,
        "comments" : "outgoing payment",
        "requestDtm" : "1503492596",
        "requestIpAddress" : "127.0.0.1",
        "inboundStatusId" : 0,
        "inboundStatusDtm" : "1503492596"
   }

   Example Response: 

   {
        "id": null,
        "code": "2100",
        "description": "Request was received",
        "statusCategory": 3
   }
  • Customer Initiated payment (Voluntary Customer Saving)
    Example Request:

    POST http://localhost:8080/inbound/requests?tenant=default
    Content-Type: application/json
    Request Body:
    {
        "id" : 1,
        "transactType" : "VOLUNTARY_SAVINGS",
        "paymentMethod" : "mobile money",
        "paymentMethodType" : "beyonic",
        "mmpId" : 1,
        "mfiId" : 1,
        "sourceRef" : "+233267881050",
        "destinationRef" : "+80000000001",
        "fineractAccNo" : "000000001",
        "fineractClientId" : 1,
        "amount" : 10,
        "transactionReason" : "Test Beyonic",
        "externalSystId" : 0,
        "comments" : "outgoing payment",
        "requestDtm" : "1503492596",
        "requestIpAddress" : "127.0.0.1",
        "inboundStatusId" : 0,
        "inboundStatusDtm" : "1503492596"
    }

   Example Response: 

   {
        "id": null,
        "code": "2100",
        "description": "Request was received",
        "statusCategory": 3
   }
  • Customer Initiated payment (Recurring Customer deposit)
    Example Request:

    POST http://localhost:8080/inbound/requests?tenant=default
    Content-Type: application/json
    Request Body:
    {
        "id" : 1,
        "transactType" : "RECURRING_DEPOSIT",
        "paymentMethod" : "mobile money",
        "paymentMethodType" : "beyonic",
        "mmpId" : 1,
        "mfiId" : 1,
        "sourceRef" : "+233267881050",
        "destinationRef" : "+80000000001",
        "fineractAccNo" : "abc000000047",
        "fineractClientId" : 1,
        "amount" : 10,
        "transactionReason" : "Test Beyonic",
        "externalSystId" : 0,
        "comments" : "outgoing payment",
        "requestDtm" : "1503492596",
        "requestIpAddress" : "127.0.0.1",
        "inboundStatusId" : 0,
        "inboundStatusDtm" : "1503492596"
    }

   Example Response: 

   {
        "id": null,
        "code": "2100",
        "description": "Request was received",
        "statusCategory": 3
   }
  • Real time loan disbursement
    Example Request:

    POST http://localhost:8080/outbound/requests?tenant=default
    Content-Type: application/json
    Request Body:
    {
        "id" : 1,
        "transactType" : "DISBURSEMENT",
        "paymentMethod" : "mobile money",
        "paymentMethodType" : "beyonic",
        "mmpId" : 1,
        "mfiId" : 1,
        "sourceRef" : "+233267881050",
        "destinationRef" : "+80000000001",
        "fineractAccNo" : "abc000000047",
        "fineractClientId" : 1,
        "amount" : 10,
        "transactionReason" : "Test Beyonic",
        "externalSystId" : 0,
        "comments" : "outgoing payment",
        "requestDtm" : "1503492596",
        "requestIpAddress" : "127.0.0.1",
        "outboundStatusId" : 0,
        "outboundStatusDtm" : "1503492596",
        "reverseStatusId" : 0,
        "reverseStatusIdDtm" : "1503492596"
}
   Example Response: 

   {
        "id": null,
        "code": "2100",
        "description": "Request was received",
        "statusCategory": 3
   }
  • Get recurring deposit account
    Example Request:

    POST http://localhost:8080/recurringDepositAccounts?tenant=default
    Content-Type: application/json
    Request Body:
    {
	    "accountNumber": "000000017"
    }


   Example Response: 

   {
        "id": 1,
        "accountNo": "RD000023",
        "externalId": "RD-23",
        "clientId": 1,
        "clientName": "Sangamesh N",
        "savingsProductId": 3,
        "savingsProductName": "RD01",
        "fieldOfficerId": 0,
        "status": {
            "id": 100,
            "code": "savingsAccountStatusType.submitted.and.pending.approval",
            "value": "Submitted and pending approval",
            "submittedAndPendingApproval": true,
            "approved": false,
            "rejected": false,
            "withdrawnByApplicant": false,
            "active": false,
            "closed": false,
            "prematureClosed": false,
            "transferInProgress": false,
            "transferOnHold": false
        },
        "timeline": {
            "submittedOnDate": [
            2014,
            3,
            1
            ],
            "submittedByUsername": "mifos",
            "submittedByFirstname": "App",
            "submittedByLastname": "Administrator"
        },
        "currency": {
            "code": "USD",
            "name": "US Dollar",
            "decimalPlaces": 2,
            "inMultiplesOf": 1,
            "displaySymbol": "$",
            "nameCode": "currency.USD",
            "displayLabel": "US Dollar ($)"
        },
        "interestCompoundingPeriodType": {
            "id": 4,
            "code": "savings.interest.period.savingsCompoundingInterestPeriodType.monthly",
            "value": "Monthly"
        },
        "interestPostingPeriodType": {
            "id": 4,
            "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly",
            "value": "Monthly"
        },
        "interestCalculationType": {
            "id": 1,
            "code": "savingsInterestCalculationType.dailybalance",
            "value": "Daily Balance"
        },
        "interestCalculationDaysInYearType": {
            "id": 365,
            "code": "savingsInterestCalculationDaysInYearType.days365",
            "value": "365 Days"
        },
        "preClosurePenalApplicable": false,
        "minDepositTerm": 3,
        "maxDepositTerm": 4,
        "minDepositTermType": {
            "id": 2,
            "code": "deposit.term.savingsPeriodFrequencyType.months",
            "value": "Months"
        },
        "maxDepositTermType": {
            "id": 3,
            "code": "deposit.term.savingsPeriodFrequencyType.years",
            "value": "Years"
        },
        "recurringDepositAmount": 100,
        "recurringDepositFrequency": 1,
        "expectedFirstDepositOnDate": [
            2014,
            4,
            2
        ],
        "recurringDepositFrequencyType": {
            "id": 2,
            "code": "recurring.deposit.savingsPeriodFrequencyType.months",
            "value": "Months"
            },
        "depositPeriod": 6,
        "depositPeriodFrequency": {
            "id": 2,
            "code": "deposit.period.savingsPeriodFrequencyType.months",
            "value": "Months"
        },
        "summary": {
            "currency": {
            "code": "USD",
            "name": "US Dollar",
            "decimalPlaces": 2,
            "inMultiplesOf": 1,
            "displaySymbol": "$",
            "nameCode": "currency.USD",
            "displayLabel": "US Dollar ($)"
            },
            "accountBalance": 0
        },
        "accountChart": {
            "id": 4,
            "fromDate": [
            2013,
            10,
            2
            ],
            "accountId": 5,
            "accountNumber": "RD000023",
            "chartSlabs": [
            {
                "id": 13,
                "periodType": {
                "id": 0,
                "code": "interestChartPeriodType.days",
                "value": "Days"
                },
                "fromPeriod": 181,
                "toPeriod": 365,
                "annualInterestRate": 5.5,
                "currency": {
                "code": "USD",
                "name": "US Dollar",
                "decimalPlaces": 2,
                "displaySymbol": "$",
                "nameCode": "currency.USD",
                "displayLabel": "US Dollar ($)"
                }
            },
            {
                "id": 12,
                "periodType": {
                "id": 0,
                "code": "interestChartPeriodType.days",
                "value": "Days"
                },
                "fromPeriod": 1,
                "toPeriod": 180,
                "annualInterestRate": 5,
                "currency": {
                "code": "USD",
                "name": "US Dollar",
                "decimalPlaces": 2,
                "displaySymbol": "$",
                "nameCode": "currency.USD",
                "displayLabel": "US Dollar ($)"
                }
            },
            {
                "id": 11,
                "periodType": {
                "id": 0,
                "code": "interestChartPeriodType.days",
                "value": "Days"
                },
                "fromPeriod": 366,
                "annualInterestRate": 6,
                "currency": {
                "code": "USD",
                "name": "US Dollar",
                "decimalPlaces": 2,
                "displaySymbol": "$",
                "nameCode": "currency.USD",
                "displayLabel": "US Dollar ($)"
                }
            }
            ],
            "periodTypes": [
            {
                "id": 0,
                "code": "interestChartPeriodType.days",
                "value": "Days"
            },
            {
                "id": 1,
                "code": "interestChartPeriodType.weeks",
                "value": "Weeks"
            },
            {
                "id": 2,
                "code": "interestChartPeriodType.months",
                "value": "Months"
            },
            {
                "id": 3,
                "code": "interestChartPeriodType.years",
                "value": "Years"
            }
            ]
        }
    }
  • Get customer loan account
    Example Request:

    POST http://localhost:8080//loans?tenant=default
    Content-Type: application/json
    Request Body:
    {
	    "accountNumber": "000000017"
    }


   Example Response: 

   {
  "id": 1,
  "accountNo": "000000001",
  "status": {
    "id": 300,
    "code": "loanStatusType.active",
    "value": "Active",
    "pendingApproval": false,
    "waitingForDisbursal": false,
    "active": true,
    "closedObligationsMet": false,
    "closedWrittenOff": false,
    "closedRescheduled": false,
    "closed": false,
    "overpaid": false
  },
  "clientId": 1,
  "clientName": "Kampala first Client",
  "clientOfficeId": 2,
  "loanProductId": 1,
  "loanProductName": "Kampala Product (with cash accounting)",
  "loanProductDescription": "Typical Kampala loan product with cash accounting enabled for testing.",
  "loanPurposeId": 22,
  "loanPurposeName": "option.HousingImprovement",
  "loanOfficerId": 2,
  "loanOfficerName": "LoanOfficer, Kampala",
  "loanType": {
    "id": 1,
    "code": "loanType.individual",
    "value": "Individual"
  },
  "currency": {
    "code": "UGX",
    "name": "Uganda Shilling",
    "decimalPlaces": 2,
    "displaySymbol": "USh",
    "nameCode": "currency.UGX",
    "displayLabel": "Uganda Shilling (USh)"
  },
  "principal": 1000000,
  "termFrequency": 12,
  "termPeriodFrequencyType": {
    "id": 2,
    "code": "termFrequency.periodFrequencyType.months",
    "value": "Months"
  },
  "numberOfRepayments": 12,
  "repaymentEvery": 1,
  "repaymentFrequencyType": {
    "id": 2,
    "code": "repaymentFrequency.periodFrequencyType.months",
    "value": "Months"
  },
  "interestRatePerPeriod": 24,
  "interestRateFrequencyType": {
    "id": 3,
    "code": "interestRateFrequency.periodFrequencyType.years",
    "value": "Per year"
  },
  "annualInterestRate": 24,
  "amortizationType": {
    "id": 1,
    "code": "amortizationType.equal.installments",
    "value": "Equal installments"
  },
  "interestType": {
    "id": 1,
    "code": "interestType.flat",
    "value": "Flat"
  },
  "interestCalculationPeriodType": {
    "id": 1,
    "code": "interestCalculationPeriodType.same.as.repayment.period",
    "value": "Same as repayment period"
  },
  "transactionProcessingStrategyId": 2,
  "timeline": {
    "submittedOnDate": [
      2012,
      4,
      3
    ],
    "submittedByUsername": "admin",
    "submittedByFirstname": "App",
    "submittedByLastname": "Administrator",
    "approvedOnDate": [
      2012,
      4,
      3
    ],
    "approvedByUsername": "admin",
    "approvedByFirstname": "App",
    "approvedByLastname": "Administrator",
    "expectedDisbursementDate": [
      2012,
      4,
      10
    ],
    "actualDisbursementDate": [
      2012,
      4,
      10
    ],
    "disbursedByUsername": "admin",
    "disbursedByFirstname": "App",
    "disbursedByLastname": "Administrator",
    "expectedMaturityDate": [
      2013,
      4,
      10
    ]
  },
  "summary": {
    "currency": {
      "code": "UGX",
      "name": "Uganda Shilling",
      "decimalPlaces": 2,
      "displaySymbol": "USh",
      "nameCode": "currency.UGX",
      "displayLabel": "Uganda Shilling (USh)"
    },
    "principalDisbursed": 1000000,
    "principalPaid": 0,
    "principalWrittenOff": 0,
    "principalOutstanding": 1000000,
    "principalOverdue": 833333.3,
    "interestCharged": 240000,
    "interestPaid": 0,
    "interestWaived": 0,
    "interestWrittenOff": 0,
    "interestOutstanding": 240000,
    "interestOverdue": 200000,
    "feeChargesCharged": 18000,
    "feeChargesDueAtDisbursementCharged": 0,
    "feeChargesPaid": 0,
    "feeChargesWaived": 0,
    "feeChargesWrittenOff": 0,
    "feeChargesOutstanding": 18000,
    "feeChargesOverdue": 15000,
    "penaltyChargesCharged": 0,
    "penaltyChargesPaid": 0,
    "penaltyChargesWaived": 0,
    "penaltyChargesWrittenOff": 0,
    "penaltyChargesOutstanding": 0,
    "penaltyChargesOverdue": 0,
    "totalExpectedRepayment": 1258000,
    "totalRepayment": 0,
    "totalExpectedCostOfLoan": 258000,
    "totalCostOfLoan": 0,
    "totalWaived": 0,
    "totalWrittenOff": 0,
    "totalOutstanding": 1258000,
    "totalOverdue": 1048333.3,
    "overdueSinceDate": [
      2012,
      5,
      10
    ],
    "linkedAccount":{
    	"id":1,
    	"accountNo":"000000001"
    },
    "disbursementDetails":[{"id":71,"expectedDisbursementDate":[2013,11,1],"principal":22000.000000,"approvedPrincipal":22000.000000}],
    "fixedEmiAmount":1100.000000,
    "maxOutstandingLoanBalance":35000,
    "canDisburse":false,
    "emiAmountVariations": [],
  "inArrears": true,
  "isNPA":false,
  "overdueCharges": [
    {
      "id": 20,
      "name": "overdraft penality",
      "active": true,
      "penalty": true,
      "currency": {
        "code": "USD",
        "name": "US Dollar",
        "decimalPlaces": 2,
        "displaySymbol": "$",
        "nameCode": "currency.USD",
        "displayLabel": "US Dollar ($)"
      },
      "amount": 3.000000,
      "chargeTimeType": {
        "id": 9,
        "code": "chargeTimeType.overdueInstallment",
        "value": "overdue fees"
      },
      "chargeAppliesTo": {
        "id": 1,
        "code": "chargeAppliesTo.loan",
        "value": "Loan"
      },
      "chargeCalculationType": {
        "id": 2,
        "code": "chargeCalculationType.percent.of.amount",
        "value": "% Amount"
      },
      "chargePaymentMode": {
        "id": 0,
        "code": "chargepaymentmode.regular",
        "value": "Regular"
      },
      "feeInterval": 2,
      "feeFrequency": {
        "id": 1,
        "code": "feeFrequencyperiodFrequencyType.weeks",
        "value": "Weeks"
      }
    }
  ]
  }
}


  • Get voluntary saving account
    Example Request:

    POST http://localhost:8080//voluntarySavingAccounts?tenant=default
    Content-Type: application/json
    Request Body:
    {
	    "accountNumber": "000000017"
    }


   Example Response: 

   {
  "id": 1,
  "accountNo": "000000001",
  "clientId": 1,
  "clientName": "small business",
  "savingsProductId": 1,
  "savingsProductName": "Passbook Savings",
  "fieldOfficerId": 0,
  "status": {
    "id": 100,
    "code": "savingsAccountStatusType.submitted.and.pending.approval",
    "value": "Submitted and pending approval",
    "submittedAndPendingApproval": true,
    "approved": false,
    "rejected": false,
    "withdrawnByApplicant": false,
    "active": false,
    "closed": false
  },
  "timeline": {
    "submittedOnDate": [
      2013,
      3,
      1
    ]
  },
  "currency": {
    "code": "USD",
    "name": "US Dollar",
    "decimalPlaces": 2,
    "displaySymbol": "$",
    "nameCode": "currency.USD",
    "displayLabel": "US Dollar ($)"
  },
  "nominalAnnualInterestRate": 5,
  "interestCompoundingPeriodType": {
    "id": 1,
    "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily",
    "value": "Daily"
  },
  "interestPostingPeriodType": {
    "id": 4,
    "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly",
    "value": "Monthly"
  },
  "interestCalculationType": {
    "id": 1,
    "code": "savingsInterestCalculationType.dailybalance",
    "value": "Daily Balance"
  },
  "interestCalculationDaysInYearType": {
    "id": 365,
    "code": "savingsInterestCalculationDaysInYearType.days365",
    "value": "365 Days"
  },
  "summary": {
    "currency": {
      "code": "USD",
      "name": "US Dollar",
      "decimalPlaces": 2,
      "displaySymbol": "$",
      "nameCode": "currency.USD",
      "displayLabel": "US Dollar ($)"
    },
    "accountBalance": 0,
	"availableBalance": 0
  }
}

API documentation for Beyonic Payment Service

  • Outgoing Payment to Beyonic
```sh

    Example Request:
    POST http://localhost:8040/outbound/payments
    Content-Type: application/json
    Request Body:

    {
        "id" : 1,
        "transactType" : "DISBURSEMENT",
        "paymentMethod" : "mobile money",
        "paymentMethodType" : "beyonic",
        "mmpId" : 1,
        "mfiId" : 1,
        "sourceRef" : "+233267881050",
        "destinationRef" : "+80000000001",
        "fineractAccNo" : "accno123",
        "fineractClientId" : 1,
        "amount" : 10,
        "transactionReason" : "Test Beyonic",
        "externalSystId" : 0,
        "comments" : "outgoing payment",
        "requestDtm" : "1503492596",
        "requestIpAddress" : "127.0.0.1",
        "outboundStatusId" : 1,
        "outboundStatusDtm" : "1503492596",
        "reverseStatusId" : 1,
        "reverseStatusIdDtm" : "1503492596"
    }

    Example Response:

     {
        "id": null,
        "code": "2100",
        "description": "Request was received",
        "statusCategory": 1
    }


* Beyonic Incoming payment from a client.

 ```sh
 
     Example Request:
     POST http://localhost:8040/collections
     Content-Type: application/json
     Request Body:

     {
         "hook": {
                     "id": 53,
                     "created": "2015-08-01T16:56:29Z",
                     "updated": "2015-08-01T16:56:29Z",
                     "event": "collection.received",
                     "target": "https://localhost:8040/collections",
                     "user": 42
                 },
         "data": {
                     "id": 82,
                     "remote_transaction_id": "1485758785",
                     "organization": 1,
                     "amount": "2000.0000",
                     "currency": 2,
                     "phonenumber": "+80000000001",
                     "payment_date": "2015-07-14T09:57:44Z",
                     "reference": "default123456789",
                     "status": "successful",
                     "created": "2015-07-14T15:19:05Z",
                     "author": null,
                     "modified": "2015-08-20T16:48:51Z",
                     "updated_by": null
                 }
     }
 
     Example Response:

     {
         "id": null,
         "code": "2100",
         "description": "Request was received",
         "statusCategory": 1
     }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment