Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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