Beispiel #1
0
def c2b_validation_url(request, organization_id=None, reference=None):
    try:
        logger.debug(request.body)
        data = json.loads(request.body)
        logger.debug(data)

        form = forms.APIC2BForm(data)
        status = 200

        if form.is_valid():
            return process_request(request,
                                   mpesa.process_c2b_validation_request,
                                   organization_id=organization_id,
                                   reference=reference)
        else:
            status = 400
            response = {
                "message": "Error in values passed",
                "success": False,
                "errors": form.errors
            }
    except (ObjectDoesNotExist, Http404) as e:
        logger.exception(e)

        status = 404
        response = {"message": "Item not found", "success": False}
    except Exception as e:
        logger.exception(e)

        response = {"message": "Internal error", "success": False}
        status = 400

    return JsonResponse(response, status=status)
Beispiel #2
0
def process_loan_repayment_request(event, context):
    """
    The Loan's Module makes a loan repayment request, we analyze the Loan 
    account for any credit dues then makes a request to mpesa for repayment.

    AWS specific
    """
    for message in event["Records"]:
        message_body = message['body']
        logger.debug(message_body)

        try:
            try:
                transaction_details = json.loads(message_body)
            except Exception as e:
                transaction_details = {}

                logger.exception(e)

            logger.debug('transaction_details')
            logger.debug(transaction_details)

            # process loan
            api.process_loan_repayment_request(**transaction_details)

            logger.debug(f"finished process_loan_repayment_request({message})")
        except Exception as e:
            logger.exception(e)

            logger.debug(f"failed process_loan_repayment_request({message})")
Beispiel #3
0
def process_request(request, processor, *args, **kwargs):
    logger.info("Processing request: %s" % processor)

    try:
        data = json.loads(request.body)
        logger.debug(data)

        result_code = processor(data, *args, **kwargs)
        if result_code == enums.ResultCode.success:
            logger.debug("Result processed successfully")
            response = {
                "ResultCode": enums.ResultCode.success,
                "ResultDesc": "Accepted"
            }
        else:
            logger.debug(f"Result not processed successfully {result_code}")
            response = {"ResultCode": result_code, "ResultDesc": "Rejected"}

        status = 200
    except Exception as e:
        logger.exception("Error processing request")

        status = 500
        response = {
            "ResultCode": enums.ResultCode.internal_failure,
            "ResultDesc": "Rejected"
        }

    logger.info("Finished processing request: %s" % processor)

    return JsonResponse(response, status=status)
Beispiel #4
0
def process_loan_application(event, context):
    """
    We analyze the loan request, and send 
    """
    for message in event["Records"]:
        message_body = message['body']
        logger.debug(message_body)

        try:
            try:
                transaction_details = json.loads(message_body)
            except Exception as e:
                transaction_details = {}

                logger.exception(e)

            logger.debug('transaction_details')
            logger.debug(transaction_details)

            if not transaction_details:
                raise RuntimeError("Cannot process this transaction")

            api.process_loan_application(**transaction_details)

            logger.debug("finished running process_loan_application()")
        except Exception as e:
            logger.exception(e)

            logger.debug(f"failed process_loan_repayment_request({message})")
Beispiel #5
0
def extract_body(request, *args, **kwargs):

    try:
        data = json.loads(request.body)
    except Exception:
        data = {}
        logger.error(json.dumps(
            dict(message="Error decoding data submitted.",
                 args=args,
                 kwargs=kwargs)),
                     exc_info=True)

    return data
Beispiel #6
0
    def test_get_user_is_accessible_via_token_successfully(self):
        response = self.client.get(reverse(
            'users:get_user',
            kwargs={'user_account_id': self.user.user_account.account_id}),
                                   HTTP_AUTHORIZATION=self.HTTP_AUTHORIZATION)

        self.assertTrue(response.status_code, HTTPStatus.OK.value)
        self.assertEqual(
            response.json(),
            json.loads(
                json.dumps({
                    'success': True,
                    'user_account': self.user_account.as_dict()
                })))
Beispiel #7
0
    def action(request):
        try:
            logger.debug(request.body)

            data = json.loads(request.body)
            logger.debug(data)

            response = api.reversal_queue_timeout_url(
                data, organization_id=organization_id, reference=reference)
            status = 200
        except Exception as e:
            logger.exception(e)

            response = {"message": "Internal error", "success": False}
            status = 400

        return JsonResponse(response, status=status)
Beispiel #8
0
def process_loan_transaction(event, context):
    for message in event["Records"]:
        logger.debug("Processing loan transaction")

        message_body = message['body']

        try:
            transaction_details = json.loads(message_body)
        except Exception as e:
            transaction_details = {}

            logger.debug(message)
            logger.debug(message_body)

            logger.exception(e)

        logger.debug(transaction_details)

        if not transaction_details:
            logger.debug("transaction_details has invalid value, exiting...")
            raise RuntimeError("Cannot process this transaction")

        amount = 0
        fee = 0
        repayment_amount = 0

        if transaction_type == LoanTransactionType.LOAN_DISBURSAL:
            amount = transaction_details['transaction']['amount']
            fee = transaction_details['transaction']['payment_platform']['fee']

        elif transaction_type == LoanTransactionType.LOAN_REPAYMENT:
            repayment_amount = transaction_details['transaction'][
                'repayment_amount']

        loan_account_id = transaction_details['loan_account_id']
        transaction_type = transaction_details['transaction'][
            'transaction_type']

        api.process_loan_transaction(loan_account_id,
                                     transaction_type,
                                     amount=amount,
                                     fee=fee,
                                     repayment_amount=repayment_amount)
Beispiel #9
0
    def action(request):
        logger.info("Processing B2C Request result for %s transaction id %s" %
                    (organization_id, reference))

        try:
            logger.debug(request.body)

            data = json.loads(request.body)
            logger.debug(data)

            response = api.b2c_result_url(data,
                                          organization_id=organization_id,
                                          reference=reference)
            status = 200

        except Exception as e:
            logger.exception(e)
            response = {"message": "Internal error", "success": False}
            status = 400

        return JsonResponse(response, status=status)
Beispiel #10
0
def process_mpesa_b2c_transaction(event, context):
    logger.info("calling process_mpesa_b2c_transaction()")

    for message in event["Records"]:
        message_body = message['body']
        logger.debug(message_body)

        try:
            try:
                transaction_details = json.loads(message_body)
            except Exception as e:
                transaction_details = {}

                logger.exception(e)

            logger.debug('transaction_details')
            logger.debug(transaction_details)

            api.process_mpesa_b2c_transaction(**transaction_details)
        except Exception as e:
            logger.exception(e)

            logger.debug(f"failed process_mpesa_b2c_transaction({message})")
Beispiel #11
0
def get_encrypted_text(plain_text, function_name=None):
    c = boto3.client(
        'lambda',
        aws_access_key_id=settings.ENCRYPTION_INVOKER_ACCESS_KEY_ID,
        aws_secret_access_key=settings.ENCRYPTION_INVOKER_SECRET_ACCESS_KEY,
        region_name="us-east-1")

    logger.info("Boto client %s prepared" % c)

    try:
        logger.debug("Invoking lambda with access key id %s" %
                     settings.ENCRYPTION_INVOKER_ACCESS_KEY_ID)
        response = c.invoke(FunctionName=function_name,
                            InvocationType='RequestResponse',
                            Payload=json.dumps({"text": plain_text}))

        logger.info("Response returned %s" % response)
        payload = json.loads(response['Payload'].read())

        return payload['encrypted']
    except (TypeError, KeyError) as e:
        logger.exception(e)
        return
Beispiel #12
0
def fund_loan_book(request):
    response = {}

    data = json.loads(request.body)
    form = FundLoanBookForm(data=data)

    if form.is_valid():
        balance = api.fund_loan_book(**data)

        if balance:
            response['success'] = True
            response['data'] = dict(balance_id=balance.entry_id,
                                    balance_as_at=balance.balance_as_at)

        else:
            response['success'] = False
            response['message'] = "Could not complete transaction"

    else:
        response['success'] = False
        response['errors'] = dict(form.errors.items())

    return json.JsonResponse(response)
Beispiel #13
0
def balance_check_queue_timeout_url(request,
                                    organization_id=None,
                                    reference=None):
    try:
        with db_transaction.atomic():
            mpesa_transaction = get_object_or_404(
                MpesaTransaction.objects.filter(
                    command_id=enums.CommandID.UTILITY_ACCOUNT_BALANCE,
                    sender_account__organization__organization_id=
                    organization_id,
                    transaction_id=reference,
                    transaction_time__isnull=True,
                    transaction_amount__isnull=True,
                    response_payload__isnull=True))

            logger.debug(request.body)
            data = json.loads(request.body)
            logger.debug(data)

            mpesa_transaction.response_payload = data

            mpesa_transaction.save()

            response = {"message": "Received successfully.", "success": True}
            status = 200
    except (ObjectDoesNotExist, Http404) as e:
        logger.exception(e)

        status = 404
        response = {"message": "Item not found", "success": False}
    except Exception as e:
        logger.exception(e)

        response = {"message": "Internal error", "success": False}
        status = 400

    return JsonResponse(response, status=status)
    def test_mpesa_user_can_register_and_borrow(self, b2c_transact,
                                                authenticate,
                                                get_encrypted_text,
                                                mpesa_express_stk_push):

        b2c_transact.return_value = {
            "ConversationID": "AG_20180326_00005ca7f7c21d608166",
            "OriginatorConversationID": "12363-1328499-6",
            "ResponseCode": "0",
            "ResponseDescription": "Accept the service request successfully."
        }
        authenticate.return_value = {
            "access_token": "JZIAHHsd43435jjKSu238007K98SO",
            "expires_in": "3599"
        }
        get_encrypted_text.return_value = {
            "encrypted": "%s==" % ("ahh2iza7823kasksa" * 7)
        }
        mpesa_express_stk_push.return_value = {
            "MerchantRequestID": "21605-295434-4",
            "CheckoutRequestID": "ws_CO_04112017184930742",
            "ResponseCode": "0",
            "ResponseDescription": "Success. Request accepted for processing",
            "CustomerMessage": "Success. Request accepted for processing"
        }

        #------------- Setup

        # 3. Now, borrow the amount
        response = self.client.post(reverse_lazy(
            'mpesa:mpesa_express_c2b_push',
            kwargs={'organization_id': self.organization.organization_id}),
                                    data={
                                        'short_code': '263801',
                                        'phone_number': self.user.username,
                                        'amount': 320,
                                        'notes': 'My notes'
                                    },
                                    HTTP_AUTHORIZATION=self.HTTP_AUTHORIZATION)
        transaction = MpesaTransaction.objects.last()
        self.assertEqual(
            response.json(),
            json.loads(
                json.dumps({
                    'success': True,
                    'transaction': transaction.as_dict()
                })))

        # 3. Simulate callback from M-Pesa
        response = self.client.post(reverse_lazy(
            'mpesa:mpesa_c2b_stk_push_callback_url',
            kwargs={
                'organization_id': self.organization.organization_id,
                'reference': transaction.third_party_transaction_id
            }),
                                    json.dumps(C2B_STK_MPESA_EXPRESS_RESPONSE),
                                    content_type="application/json")

        self.assertEqual(response.json(), {
            'ResultCode': 0,
            'ResultDesc': 'Accepted'
        })
Beispiel #15
0
def loan_repayment(request, loan_account_id=None):
    response = {}

    try:
        loan_account = api\
            .get_active_loan_account(request.user, loan_account_id=loan_account_id)

        if loan_account.status in (enums.LoanStatus.PAID_OFF,
                                   enums.LoanStatus.WRITTEN_OFF,
                                   enums.LoanStatus.CLOSED):
            response.update({
                'loan_balance':
                0,
                'success':
                False,
                'message':
                "Loan repayment unsuccessful. Your loan has already been closed."
            })
            status = HTTPStatus.PRECONDITION_REQUIRED

        elif loan_account.status in (enums.LoanStatus.ACTIVE,
                                     enums.LoanStatus.DISBURSED,
                                     enums.LoanStatus.IN_ARREARS):
            logger.debug(f"checking request.body = {request.body}")
            data = loads(request.body)

            repayment_amount = D(
                data.get('repayment_amount')
                or loan_account.outstanding_balance or 0)
            loan_transaction = queue.notify_loan_repayment_request(
                loan_account, repayment_amount)

            response.update({
                'success':
                True,
                'transaction':
                dict(transaction_id=loan_transaction.transaction_id,
                     loan_account_id=loan_transaction.loan_account.account_id,
                     amount=loan_transaction.amount),
                'message':
                "Loan repayment request successful. Kindly input the M-Pesa password to continue"
            })

            status = HTTPStatus.OK
        else:
            response.update({
                'success':
                False,
                'message':
                "Loan repayment request unsuccessful. Invalid Loan state."
            })

            status = HTTPStatus.EXPECTATION_FAILED
    except (ObjectDoesNotExist, Http404) as e:
        logger.exception(e)

        response.update({
            "message": "Please complete system setup first",
            "success": False
        })
        status = HTTPStatus.NOT_FOUND
    except Exception as e:
        logger.exception(e)

        response.update({"message": "Internal Error", "success": False})
        status = HTTPStatus.INTERNAL_SERVER_ERROR

    return JsonResponse(response, status=status)
Beispiel #16
0
def loan_application(request, loan_profile_id=None):
    logger.info("Request for borrow by %s" % request.user.username)
    response = {}

    try:
        loan_profile = api.get_loan_profile(loan_profile_id=loan_profile_id)

        if (loan_profile.loan_accounts.filter(status__in=(
                enums.LoanStatus.ACTIVE, enums.LoanStatus.DISBURSED)).exists()
                or loan_profile.status in (enums.LoanProfileStatus.DEFAULT,
                                           enums.LoanProfileStatus.BLACKLISTED,
                                           enums.LoanProfileStatus.SUSPENDED)):
            response.update({
                'loan_balance':
                0,
                'success':
                False,
                'message':
                'Loan application unsuccessful; You currently do not meet the requirements for a new Loan.'
            })
            status = HTTPStatus.PRECONDITION_REQUIRED

        elif (loan_profile.loan_accounts.filter(status__in=(
                enums.LoanStatus.CLEAN, enums.LoanStatus.PENDING_DISBURSEMENT,
                enums.LoanStatus.PAID_OFF)).exists() and loan_profile.status
              not in (enums.LoanProfileStatus.DEFAULT,
                      enums.LoanProfileStatus.BLACKLISTED,
                      enums.LoanProfileStatus.SUSPENDED)):
            try:
                logger.debug(f"checking request.body = {request.body}")
                data = loads(request.body)

                loan_amount = data.get('loan_amount', loan_profile.loan_limit)
                if loan_amount:
                    loan_application = queue.notify_loan_application(
                        loan_profile, loan_amount)

                    if loan_application:
                        status = HTTPStatus.OK
                        logger.info("Request for borrow by %s successful" %
                                    request.user.username)

                        response.update({
                            'application':
                            loan_application.as_dict(),
                            'success':
                            True,
                            'message':
                            'Loan application successful; Wait for an M-Pesa confirmation or rejection notice.'
                        })
                else:
                    response.update({
                        'success': False,
                        'message': "Invalid Loan amount."
                    })
                    status = HTTPStatus.EXPECTATION_FAILED
            except Exception as e:
                logger.exception(e)

                response.update({
                    'success': False,
                    'message': "Invalid Loan state."
                })
                status = HTTPStatus.EXPECTATION_FAILED
        else:
            response.update({
                'success': False,
                'message': "Invalid Loan state."
            })
            status = HTTPStatus.EXPECTATION_FAILED

    except (ObjectDoesNotExist, Http404) as e:
        logger.exception(e)
        response.update({
            "message": "Please complete system setup first",
            "success": False
        })
        status = HTTPStatus.NOT_FOUND
    except Exception as e:
        logger.exception(e)
        response.update({"message": "Internal Error", "success": False})
        status = HTTPStatus.INTERNAL_SERVER_ERROR

    return JsonResponse(response, status=status)