Beispiel #1
0
def check_invoice_status(invoice_token):

    headers = _create_headers()
    endpoint = MPOWER_ENDPOINT_CHECK_INVOICE_STATUS.format(invoice_token)
    try:
        response = requests.get(endpoint, headers=headers)
        decoded_response = response.json()

        if decoded_response['response_code'] != MPOWER_RESPONSE_SUCCESS:
            message = 'ERROR - MPOWER (check_invoice_status for token {}): transaction not found'
            raise MPowerException

        if decoded_response['status'] != MPOWER_STATUS_COMPLETED:
            message = 'ERROR - MPOWER (check_invoice_status for token {}): transaction not completed'
            raise MPowerException

        return True

    except (RequestException, KeyError, ValueError) as e:
        message = 'ERROR - MPOWER (check_invoice_status for token {}): {}'
        log_error(message.format(invoice_token, repr(e)))
    except MPowerException:
        log_error(message.format(invoice_token))

    return False
Beispiel #2
0
 def end_previous_pricing():
     try:
         previous_pricing = Pricing.objects.get(end__isnull=True)
         previous_pricing.end = timezone.now()
         previous_pricing.save()
     except ObjectDoesNotExist:
         log_error('ERROR - Failed to end previous pricing.')
Beispiel #3
0
def _send_message(mobile_number, content):

    headers = {'content-type': 'application/json', 'Host': 'api.smsgh.com'}

    payload = {
        'From': 'Kitiwa',
        'To': mobile_number,
        'Content': content,
        'RegisteredDelivery': 'true'
    }
    try:

        response = requests.post(SMSGH_SEND_MESSAGE,
                                 data=json.dumps(payload),
                                 headers=headers,
                                 auth=HTTPBasicAuth(SMSGH_CLIENT_ID,
                                                    SMSGH_CLIENT_SECRET))

        decoded_response = response.json()

        response_status = decoded_response['Status']

        message_id = decoded_response['MessageId']
    except KeyError:
        message = 'ERROR - SMSGH: Failed to send message to {}. '\
                  'Status: {}. Message: {}.'
        log_error(message.format(mobile_number, response_status, content))
        message_id = 'N/A'
    except (RequestException, ValueError) as e:
        message = 'ERROR - SMSGH: Failed to send message to {}.({}).'
        log_error(message.format(mobile_number, repr(e)))
        message_id = response_status = 'N/A'

    return response_status, message_id
Beispiel #4
0
def check_balance():

    payload = {
        'api_id': SMSGH_CLIENT_ID,
        'user': SMSGH_USER,
        'password': SMSGH_PASSWORD
    }
    try:
        response = requests.get(SMSGH_CHECK_BALANCE, params=payload)

        balance = float(re.search(r'\d+.\d+', response.text).group(0))
    except (RequestException, IndexError) as e:
        message = 'ERROR - SMSGH: Failed to check balance ({}).'
        log_error(message.format(repr(e)))
        return

    return balance
Beispiel #5
0
def send_mail_to_admins(subject, body):

    sg = sendgrid.SendGridClient(SENDGRID_USERNAME, SENDGRID_PASSWORD)

    recipients = User.objects.filter(is_staff=True)
    mails = [m.email for m in recipients]

    message = sendgrid.Mail()
    message.add_to(mails)
    message.set_from(SENDGRID_EMAIL_FROM)
    message.set_subject(subject)
    message.set_text(body)

    try:
        sg.send(message)
    except sendgrid.SendGridError as e:
        log_error('ERROR - Sendgrid: Failed to send mail to admins ({})'.format(e))
        pass
Beispiel #6
0
def send_mail_to_admins(subject, body):

    sg = sendgrid.SendGridClient(SENDGRID_USERNAME, SENDGRID_PASSWORD)

    recipients = User.objects.filter(is_staff=True)
    mails = [m.email for m in recipients]

    message = sendgrid.Mail()
    message.add_to(mails)
    message.set_from(SENDGRID_EMAIL_FROM)
    message.set_subject(subject)
    message.set_text(body)

    try:
        sg.send(message)
    except sendgrid.SendGridError as e:
        log_error(
            'ERROR - Sendgrid: Failed to send mail to admins ({})'.format(e))
        pass
Beispiel #7
0
def opr_token_request(amount,
                      mpower_phone_number,
                      invoice_desc='',
                      store_name='Kitiwa'):

    # convert to local phone number format
    mpower_phone_number = '0{}'.format(mpower_phone_number[4::])

    payload = {
        'invoice_data': {
            'invoice': {
                'total_amount': amount,
                'description': invoice_desc
            },
            'store': {
                'name': store_name
            }
        },
        'opr_data': {
            'account_alias': mpower_phone_number
        }
    }

    headers = _create_headers()
    try:
        response = requests.post(MPOWER_ENDPOINT_OPR_TOKEN_REQUEST,
                                 data=json.dumps(payload),
                                 headers=headers)

        decoded_response = response.json()
        response_code = decoded_response['response_code']
        response_text = decoded_response['response_text']

        opr_token = decoded_response['token']
        invoice_token = decoded_response['invoice_token']
    except KeyError as e:
        opr_token = invoice_token = 'N/A'
    except RequestException as e:
        message = 'ERROR - MPOWER (opr_token_request for no: {}): {}'
        log_error(message.format(mpower_phone_number, repr(e)))
        response_code = opr_token = invoice_token = 'N/A'
        response_text = repr(e)
    return response_code, response_text, opr_token, invoice_token
Beispiel #8
0
def opr_charge_action(opr_token, confirm_token):

    headers = _create_headers()

    payload = {'token': opr_token, 'confirm_token': confirm_token}

    try:
        response = requests.post(MPOWER_ENDPOINT_OPR_TOKEN_CHARGE,
                                 data=json.dumps(payload),
                                 headers=headers)

        decoded_response = response.json()

        response_code = decoded_response['response_code']
        response_text = decoded_response['response_text']

    except (RequestException, KeyError) as e:
        message = 'ERROR - MPOWER (opr_charge_action for token {}): {}'
        log_error(message.format(opr_token, repr(e)))
        response_code = "N/A"
        response_text = repr(e)

    return response_code, response_text
Beispiel #9
0
def get_bitstamp_exchange_rate():
    try:
        get_rate_call = requests.get(s.BITSTAMP_API_TICKER)
        if get_rate_call.status_code == 200:
            try:
                return get_rate_call.json().get('ask')
            except AttributeError:
                log_error('ERROR - BITSTAMP: Ask rate not present in 200 response')
                return None
        else:
            log_error('ERROR - BITSTAMP: Call returned response code: ' + str(get_rate_call.status_code))
            return None
    except requests.RequestException as e:
        log_error('ERROR - BLOCKCHAIN: Call gave a request exception ' + repr(e))
        return None
Beispiel #10
0
 def post(self, request):
     params = self.get_params(request)
     if None in params.values():
         log_error('ERROR - BITSTAMP (' + self.url + '): Missing nonce/signature in params')
         return Response({'error': 'Invalid data received'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
     try:
         r = requests.post(self.url, data=params)
         if r.status_code == 200:
             return Response(r.json())
         else:
             log_error('ERROR - BITSTAMP: Call returned response code: ' + str(r.status_code))
             return Response(r.json(), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
     except requests.RequestException as e:
         log_error('ERROR - BITSTAMP: Call gave a request exception ' + repr(e))
         return Response({'error': 'Unable to retrieve balance (request exception)'},
                         status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Beispiel #11
0
def get_balance(request):
    password = request.POST.get('password', None)

    if password is None:
        return Response({'error': 'Password required'},
                        status=status.HTTP_400_BAD_REQUEST)

    try:
        r = requests.get(s.BLOCKCHAIN_API_BALANCE,
                         params={'password': password})
        if r.status_code == 200:
            try:
                btc = decimal.Decimal(r.json().get('balance')) / s.ONE_SATOSHI
            except AttributeError:
                log_error(
                    'ERROR - BLOCKCHAIN (get_balance): "balance" not present in 200 response'
                )
                return Response(r.json(), status=status.HTTP_403_FORBIDDEN)

            rate = get_blockchain_exchange_rate()
            if rate is not None:
                usd = btc * decimal.Decimal(rate)
                return Response({
                    'btc': btc,
                    'usd': '{0:.2f}'.format(usd),
                    'rate': rate
                })
            else:
                return Response({
                    'btc': btc,
                    'usd': 'Unable to retrieve rate',
                    'rate': 'Unable to retrieve rate'
                })
        else:
            log_error(
                'ERROR - BLOCKCHAIN (get_balance): Call returned response code: '
                + str(r.status_code))
            return Response(
                {'error': 'Unable to retrieve balance (non-200 response)'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except requests.RequestException as e:
        log_error(
            'ERROR - BLOCKCHAIN (get_balance): Call gave a request exception '
            + repr(e))
        return Response(
            {'error': 'Unable to retrieve balance (request exception)'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Beispiel #12
0
def get_bitstamp_exchange_rate():
    try:
        get_rate_call = requests.get(s.BITSTAMP_API_TICKER)
        if get_rate_call.status_code == 200:
            try:
                return get_rate_call.json().get('ask')
            except AttributeError:
                log_error(
                    'ERROR - BITSTAMP: Ask rate not present in 200 response')
                return None
        else:
            log_error('ERROR - BITSTAMP: Call returned response code: ' +
                      str(get_rate_call.status_code))
            return None
    except requests.RequestException as e:
        log_error('ERROR - BLOCKCHAIN: Call gave a request exception ' +
                  repr(e))
        return None
Beispiel #13
0
def get_blockchain_exchange_rate():
    try:
        get_rate_call = requests.get(s.BLOCKCHAIN_TICKER)
        if get_rate_call.status_code == 200:
            try:
                return get_rate_call.json().get('USD').get('buy')
            except AttributeError:
                log_error(
                    'ERROR - BLOCKCHAIN (get_blockchain_exchange_rate): USD[buy] not present in 200 response'
                )
                return None
        else:
            log_error(
                'ERROR - BLOCKCHAIN (get_blockchain_exchange_rate): Call returned response code: '
                + str(get_rate_call.status_code))
            return None
    except requests.RequestException as e:
        log_error(
            'ERROR - BLOCKCHAIN (get_blockchain_exchange_rate): Call gave a request exception '
            + repr(e))
        return None
Beispiel #14
0
 def post(self, request):
     params = self.get_params(request)
     if None in params.values():
         log_error('ERROR - BITSTAMP (' + self.url +
                   '): Missing nonce/signature in params')
         return Response({'error': 'Invalid data received'},
                         status=status.HTTP_500_INTERNAL_SERVER_ERROR)
     try:
         r = requests.post(self.url, data=params)
         if r.status_code == 200:
             return Response(r.json())
         else:
             log_error('ERROR - BITSTAMP: Call returned response code: ' +
                       str(r.status_code))
             return Response(r.json(),
                             status=status.HTTP_500_INTERNAL_SERVER_ERROR)
     except requests.RequestException as e:
         log_error('ERROR - BITSTAMP: Call gave a request exception ' +
                   repr(e))
         return Response(
             {'error': 'Unable to retrieve balance (request exception)'},
             status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Beispiel #15
0
def get_blockchain_exchange_rate():
    try:
        get_rate_call = requests.get(s.BLOCKCHAIN_TICKER)
        if get_rate_call.status_code == 200:
            try:
                return get_rate_call.json().get('USD').get('buy')
            except AttributeError:
                log_error(
                    'ERROR - BLOCKCHAIN (get_blockchain_exchange_rate): USD[buy] not present in 200 response'
                )
                return None
        else:
            log_error(
                'ERROR - BLOCKCHAIN (get_blockchain_exchange_rate): Call returned response code: ' +
                str(get_rate_call.status_code)
            )
            return None
    except requests.RequestException as e:
        log_error(
            'ERROR - BLOCKCHAIN (get_blockchain_exchange_rate): Call gave a request exception ' +
            repr(e)
        )
        return None
Beispiel #16
0
def get_balance(request):
    password = request.POST.get('password', None)

    if password is None:
        return Response({'error': 'Password required'}, status=status.HTTP_400_BAD_REQUEST)

    try:
        r = requests.get(s.BLOCKCHAIN_API_BALANCE, params={'password': password})
        if r.status_code == 200:
            try:
                btc = decimal.Decimal(r.json().get('balance')) / s.ONE_SATOSHI
            except AttributeError:
                log_error(
                    'ERROR - BLOCKCHAIN (get_balance): "balance" not present in 200 response'
                )
                return Response(r.json(), status=status.HTTP_403_FORBIDDEN)

            rate = get_blockchain_exchange_rate()
            if rate is not None:
                usd = btc * decimal.Decimal(rate)
                return Response({'btc': btc, 'usd': '{0:.2f}'.format(usd), 'rate': rate})
            else:
                return Response({'btc': btc, 'usd': 'Unable to retrieve rate', 'rate': 'Unable to retrieve rate'})
        else:
            log_error(
                'ERROR - BLOCKCHAIN (get_balance): Call returned response code: ' +
                str(r.status_code)
            )
            return Response({'error': 'Unable to retrieve balance (non-200 response)'},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except requests.RequestException as e:
        log_error(
            'ERROR - BLOCKCHAIN (get_balance): Call gave a request exception ' +
            repr(e)
        )
        return Response({'error': 'Unable to retrieve balance (request exception)'},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Beispiel #17
0
def get_usd_ghs(request):
    try:
        r = requests.get(s.OPEN_EXCHANGE_RATE_API_URL)
        if r.status_code == 200:
            try:
                rate = r.json().get('rates').get('GHS')
                return Response({'rate': rate})
            except AttributeError:
                log_error(
                    'ERROR - FOREX (get_usd_ghs): rates[GHS] not present in 200 response'
                )
                return Response(r.json(), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        else:
            log_error(
                'ERROR - FOREX (get_usd_ghs): Call returned response code: ' +
                str(r.status_code)
            )
            return Response(r.json(), status=r.status_code)
    except requests.RequestException as e:
        log_error(
            'ERROR - FOREX (get_usd_ghs): Call gave a request exception ' +
            repr(e)
        )
        return Response(r.json(), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Beispiel #18
0
def backend_callback(request):

    try:
        invoice = request.DATA.get('invoice')
        transaction_id = request.DATA.get('transaction_id')
        transaction_reference = request.DATA.get('transaction_reference')

        notification_private_key = request.DATA.get('notification_private_key')
        merchant_key = request.DATA.get('merchant_key')

        amount = float(request.DATA.get('amount'))
        transaction_datetime = request.DATA.get('transaction_datetime')

        # find transaction
        transaction = Transaction.objects.get(transaction_uuid=invoice)
        if transaction.state != Transaction.INIT:
            message = 'ERROR - PAGA (backend): request refers to transaction {} in state {}. {}'
            message = message.format(transaction.id, transaction.state,
                                     request.DATA)
            raise PagaException

        # validate merchant key
        if merchant_key != PAGA_MERCHANT_KEY:
            message = 'ERROR - PAGA (backend): request with invalid merchant key ({}) for transaction {}. {}'
            message = message.format(merchant_key, transaction.id,
                                     request.DATA)
            raise PagaException

        # validate private key
        if notification_private_key != PAGA_PRIVATE_KEY:
            message = 'ERROR - PAGA (backend): request with invalid private key ({}) for transaction {}. {}'
            message = message.format(notification_private_key, transaction.id,
                                     request.DATA)
            raise PagaException

        # double check amount
        if amount != transaction.amount_ngn:
            message = 'ERROR - PAGA (backend): amount for transaction {} does not match database value (db: {}, paga: {}). {}'
            message = message.format(transaction.id, transaction.amount_ngn,
                                     amount, request.DATA)
            raise PagaException

        # create PagaPayment
        paga_payment = PagaPayment(
            transaction=transaction,
            paga_transaction_reference=transaction_reference,
            paga_transaction_id=transaction_id,
            paga_processed_at=transaction_datetime,
            status='SUCCESS')

        # update transaction and paga payment (all-or-nothing)
        with dbtransaction.atomic():
            transaction.set_paid()
            paga_payment.save()

        sendgrid_mail.notify_admins_paid()

        return Response({'detail': 'Success'})

    except PagaException as e:
        log_error(message)

    except Transaction.DoesNotExist as e:
        message = 'ERROR - PAGA (backend): no transaction found for uuid {}, {}. {}'
        log_error(message.format(invoice, e, request.DATA))

    except (KeyError, TypeError, ValueError) as e:
        message = 'ERROR - PAGA (backend): received invalid payment notification, {}, {}'
        log_error(message.format(e, request.DATA))

    return Response({'detail': 'Error'}, status.HTTP_400_BAD_REQUEST)
Beispiel #19
0
def user_callback(request):
    try:
        paga_status = request.DATA.get('status')
        merchant_key = request.DATA.get('key')
        transaction_id = request.DATA.get('transaction_id')
        process_code = request.DATA.get('process_code')
        invoice = request.DATA.get('invoice')

        kitiwa_reference = request.GET.get('reference', 'error')

        # could be used for double checking the value
        # total = request.DATA.get('total')

        # not needed for now
        # fee = request.DATA.get('fee')
        # test = request.DATA.get('test')
        # message = request.DATA.get('message')
        # exchangeRate = request.DATA.get('exchange_rate')
        # reference_number = request.DATA.get('reference_number')
        # currency = request.DATA.get('currency')
        # reference = request.DATA.get('reference')
        # customer_account = request.DATA.get('customer_account')

        http_prefix = 'https://'
        if ENV == ENV_LOCAL:
            http_prefix = 'http://'

        #  incomplete request
        if (paga_status is None or merchant_key is None
                or transaction_id is None or process_code is None
                or invoice is None):
            raise ValueError

        # incorrect merchant key
        if merchant_key != PAGA_MERCHANT_KEY and paga_status != 'ERROR_AUTHENTICATION':
            raise PagaException
            # return redirect(http_prefix + ENV_SITE_MAPPING[ENV][SITE_USER] + '/#!/failed?error=merchantkey')

        # successful payment
        if paga_status == 'SUCCESS':
            return redirect(http_prefix + ENV_SITE_MAPPING[ENV][SITE_USER] +
                            '/#!/thanks?reference=' + kitiwa_reference +
                            '&pagaTransactionId=' + transaction_id)

        # failed payment
        else:
            # in case of erros during authentication with paga, response contains mostly blanks and cannot be persisted
            if paga_status != 'ERROR_AUTHENTICATION':
                # TODO: put this in messaging queue
                transaction = Transaction.objects.get(transaction_uuid=invoice,
                                                      state=Transaction.INIT)
                paga_payment = PagaPayment(
                    transaction=transaction,
                    paga_transaction_reference=process_code,
                    paga_transaction_id=transaction_id,
                    status=paga_status)
                with dbtransaction.atomic():
                    transaction.set_declined()
                    paga_payment.save()

            return redirect(http_prefix + ENV_SITE_MAPPING[ENV][SITE_USER] +
                            '/#!/failed?reference=' + kitiwa_reference +
                            '&status=' + paga_status)

    except (TypeError, ValueError) as e:
        message = 'ERROR - PAGA (user redirect): received invalid payment notification, {}, {}'
        log_error(message.format(e, request.DATA))
    except Transaction.DoesNotExist as e:
        message = 'ERROR - PAGA (user redirect): no transaction in state INIT found for uuid {}, {}. {}'
        log_error(message.format(invoice, e, request.DATA))
    except PagaException:
        message = 'ERROR - PAGA (user redirect): request with invalid merchant key ({}) for transaction {}. {}'
        log_error(message.format(merchant_key, transaction_id, request.DATA))

    # TODO: better to put a redirect here as well?
    # return redirect(http_prefix + ENV_SITE_MAPPING[ENV][SITE_USER] + '/#!/failed?error=unknown')
    return Response({'detail': 'Error'}, status.HTTP_400_BAD_REQUEST)
Beispiel #20
0
def process_transactions(ids, password1, password2):
# TODO: security concerns sending pw1, pw2?
# TODO: need to make this a transaction for atomicity or isolation?

    try:
        transactions = Transaction.objects.filter(id__in=ids)
    except Transaction.DoesNotExist:
        log_error('ERROR - ACCEPT: Invalid ID')
        return

    try:
        # Verify payment with payment provider
        for t in transactions:
            if not t.verify_payment():
                raise AcceptException(
                    'ERROR - ACCEPT: Transaction {} could not be verified as paid'.format(t.id)
                )

        # Make sure that there are enough credits in smsgh account to send out confirmation sms
        smsgh_balance = smsgh.check_balance()

        if smsgh_balance is None:
            raise AcceptException('ERROR - ACCEPT: Failed to query smsgh balance')
        elif smsgh_balance < len(transactions):
            raise AcceptException('ERROR - ACCEPT: Not enough credit on SMSGH account')

        # USD-BTC CONVERSION
        # Get latest exchange rate
        rate = get_blockchain_exchange_rate()
        if rate is None:
            raise AcceptException('ERROR - ACCEPT: Failed to retrieve exchange rate')

        # Update amount_btc based on latest exchange rate
        for t in transactions:
            t.update_btc(rate)

        # Combine transactions with same wallet address
        combined_transactions = utils.consolidate_transactions(transactions)

        # Prepare request and send
        recipients = utils.create_recipients_string(combined_transactions)

        btc_transfer_request = requests.get(BLOCKCHAIN_API_SENDMANY, params={
            'password': password1,
            'second_password': password2,
            'recipients': recipients,
            'note': BITCOIN_NOTE
        })

        if btc_transfer_request.json().get('error'):
            raise AcceptException(
                'ERROR - ACCEPT (btc transfer request to blockchain): {}'
                .format(btc_transfer_request.json())
            )

        transactions.update(state=Transaction.PROCESSED, processed_at=timezone.now())

    except AcceptException as e:
        log_error(e)
        transactions.update(state=Transaction.PAID)
        return repr(e)

    except requests.RequestException as e:
        message = 'ERROR - ACCEPT (btc transfer request to blockchain): {}'.format(repr(e))
        log_error(message)
        transactions.update(state=Transaction.PAID)
        return message

    combined_sms_confirm = utils.consolidate_notification_sms(transactions)

    # send out confirmation SMS
    for number, reference_numbers in combined_sms_confirm.iteritems():
        response_status, message_id = smsgh.send_message_confirm(
            mobile_number=number,
            reference_numbers=reference_numbers
        )

        for t in transactions.filter(notification_phone_number=number):
            t.update_after_sms_notification(
                response_status, message_id
            )

    return 'SUCCESS'
Beispiel #21
0
def process_transactions(ids, password1, password2):
    # TODO: security concerns sending pw1, pw2?
    # TODO: need to make this a transaction for atomicity or isolation?

    try:
        transactions = Transaction.objects.filter(id__in=ids)
    except Transaction.DoesNotExist:
        log_error('ERROR - ACCEPT: Invalid ID')
        return

    try:
        # Verify payment with payment provider
        for t in transactions:
            if not t.verify_payment():
                raise AcceptException(
                    'ERROR - ACCEPT: Transaction {} could not be verified as paid'
                    .format(t.id))

        # Make sure that there are enough credits in smsgh account to send out confirmation sms
        smsgh_balance = smsgh.check_balance()

        if smsgh_balance is None:
            raise AcceptException(
                'ERROR - ACCEPT: Failed to query smsgh balance')
        elif smsgh_balance < len(transactions):
            raise AcceptException(
                'ERROR - ACCEPT: Not enough credit on SMSGH account')

        # USD-BTC CONVERSION
        # Get latest exchange rate
        rate = get_blockchain_exchange_rate()
        if rate is None:
            raise AcceptException(
                'ERROR - ACCEPT: Failed to retrieve exchange rate')

        # Update amount_btc based on latest exchange rate
        for t in transactions:
            t.update_btc(rate)

        # Combine transactions with same wallet address
        combined_transactions = utils.consolidate_transactions(transactions)

        # Prepare request and send
        recipients = utils.create_recipients_string(combined_transactions)

        btc_transfer_request = requests.get(BLOCKCHAIN_API_SENDMANY,
                                            params={
                                                'password': password1,
                                                'second_password': password2,
                                                'recipients': recipients,
                                                'note': BITCOIN_NOTE
                                            })

        if btc_transfer_request.json().get('error'):
            raise AcceptException(
                'ERROR - ACCEPT (btc transfer request to blockchain): {}'.
                format(btc_transfer_request.json()))

        transactions.update(state=Transaction.PROCESSED,
                            processed_at=timezone.now())

    except AcceptException as e:
        log_error(e)
        transactions.update(state=Transaction.PAID)
        return repr(e)

    except requests.RequestException as e:
        message = 'ERROR - ACCEPT (btc transfer request to blockchain): {}'.format(
            repr(e))
        log_error(message)
        transactions.update(state=Transaction.PAID)
        return message

    combined_sms_confirm = utils.consolidate_notification_sms(transactions)

    # send out confirmation SMS
    for number, reference_numbers in combined_sms_confirm.iteritems():
        response_status, message_id = smsgh.send_message_confirm(
            mobile_number=number, reference_numbers=reference_numbers)

        for t in transactions.filter(notification_phone_number=number):
            t.update_after_sms_notification(response_status, message_id)

    return 'SUCCESS'
Beispiel #22
0
def user_callback(request):
    try:
        paga_status = request.DATA.get('status')
        merchant_key = request.DATA.get('key')
        transaction_id = request.DATA.get('transaction_id')
        process_code = request.DATA.get('process_code')
        invoice = request.DATA.get('invoice')

        kitiwa_reference = request.GET.get('reference', 'error')

        # could be used for double checking the value
        # total = request.DATA.get('total')

        # not needed for now
        # fee = request.DATA.get('fee')
        # test = request.DATA.get('test')
        # message = request.DATA.get('message')
        # exchangeRate = request.DATA.get('exchange_rate')
        # reference_number = request.DATA.get('reference_number')
        # currency = request.DATA.get('currency')
        # reference = request.DATA.get('reference')
        # customer_account = request.DATA.get('customer_account')

        http_prefix = 'https://'
        if ENV == ENV_LOCAL:
            http_prefix = 'http://'

        #  incomplete request
        if (paga_status is None or merchant_key is None or transaction_id is None
                or process_code is None or invoice is None):
            raise ValueError

        # incorrect merchant key
        if merchant_key != PAGA_MERCHANT_KEY and paga_status != 'ERROR_AUTHENTICATION':
            raise PagaException
            # return redirect(http_prefix + ENV_SITE_MAPPING[ENV][SITE_USER] + '/#!/failed?error=merchantkey')

        # successful payment
        if paga_status == 'SUCCESS':
            return redirect(http_prefix + ENV_SITE_MAPPING[ENV][SITE_USER]
                            + '/#!/thanks?reference=' + kitiwa_reference
                            + '&pagaTransactionId=' + transaction_id)

        # failed payment
        else:
            # in case of erros during authentication with paga, response contains mostly blanks and cannot be persisted
            if paga_status != 'ERROR_AUTHENTICATION':
                # TODO: put this in messaging queue
                transaction = Transaction.objects.get(transaction_uuid=invoice, state=Transaction.INIT)
                paga_payment = PagaPayment(
                    transaction=transaction, paga_transaction_reference=process_code,
                    paga_transaction_id=transaction_id, status=paga_status)
                with dbtransaction.atomic():
                    transaction.set_declined()
                    paga_payment.save()

            return redirect(http_prefix + ENV_SITE_MAPPING[ENV][SITE_USER] +
                            '/#!/failed?reference=' + kitiwa_reference +
                            '&status=' + paga_status)

    except (TypeError, ValueError) as e:
        message = 'ERROR - PAGA (user redirect): received invalid payment notification, {}, {}'
        log_error(message.format(e, request.DATA))
    except Transaction.DoesNotExist as e:
        message = 'ERROR - PAGA (user redirect): no transaction in state INIT found for uuid {}, {}. {}'
        log_error(message.format(invoice, e, request.DATA))
    except PagaException:
        message = 'ERROR - PAGA (user redirect): request with invalid merchant key ({}) for transaction {}. {}'
        log_error(message.format(merchant_key, transaction_id, request.DATA))

    # TODO: better to put a redirect here as well?
    # return redirect(http_prefix + ENV_SITE_MAPPING[ENV][SITE_USER] + '/#!/failed?error=unknown')
    return Response({'detail': 'Error'}, status.HTTP_400_BAD_REQUEST)
Beispiel #23
0
def backend_callback(request):

    try:
        invoice = request.DATA.get('invoice')
        transaction_id = request.DATA.get('transaction_id')
        transaction_reference = request.DATA.get('transaction_reference')

        notification_private_key = request.DATA.get('notification_private_key')
        merchant_key = request.DATA.get('merchant_key')

        amount = float(request.DATA.get('amount'))
        transaction_datetime = request.DATA.get('transaction_datetime')

        # find transaction
        transaction = Transaction.objects.get(transaction_uuid=invoice)
        if transaction.state != Transaction.INIT:
            message = 'ERROR - PAGA (backend): request refers to transaction {} in state {}. {}'
            message = message.format(transaction.id, transaction.state, request.DATA)
            raise PagaException

        # validate merchant key
        if merchant_key != PAGA_MERCHANT_KEY:
            message = 'ERROR - PAGA (backend): request with invalid merchant key ({}) for transaction {}. {}'
            message = message.format(merchant_key, transaction.id, request.DATA)
            raise PagaException

        # validate private key
        if notification_private_key != PAGA_PRIVATE_KEY:
            message = 'ERROR - PAGA (backend): request with invalid private key ({}) for transaction {}. {}'
            message = message.format(notification_private_key, transaction.id, request.DATA)
            raise PagaException

        # double check amount
        if amount != transaction.amount_ngn:
            message = 'ERROR - PAGA (backend): amount for transaction {} does not match database value (db: {}, paga: {}). {}'
            message = message.format(transaction.id, transaction.amount_ngn, amount, request.DATA)
            raise PagaException

        # create PagaPayment
        paga_payment = PagaPayment(
            transaction=transaction, paga_transaction_reference=transaction_reference,
            paga_transaction_id=transaction_id, paga_processed_at=transaction_datetime, status='SUCCESS')

        # update transaction and paga payment (all-or-nothing)
        with dbtransaction.atomic():
            transaction.set_paid()
            paga_payment.save()

        sendgrid_mail.notify_admins_paid()

        return Response({'detail': 'Success'})

    except PagaException as e:
        log_error(message)

    except Transaction.DoesNotExist as e:
        message = 'ERROR - PAGA (backend): no transaction found for uuid {}, {}. {}'
        log_error(message.format(invoice, e, request.DATA))

    except (KeyError, TypeError, ValueError) as e:
        message = 'ERROR - PAGA (backend): received invalid payment notification, {}, {}'
        log_error(message.format(e, request.DATA))

    return Response({'detail': 'Error'}, status.HTTP_400_BAD_REQUEST)