def get_fee(self): """ Return the fee that is withheld on this payment. This is depended on the payment method. Actual percentages are stored in the client properties """ if not hasattr(settings, 'DOCDATA_FEES'): raise PaymentException("Missing fee DOCDATA_FEES") fees = settings.DOCDATA_FEES if not fees.get('transaction'): raise PaymentException("Missing fee 'transaction'") if not fees.get('payment_methods'): raise PaymentException("Missing fee 'payment_methods'") transaction_fee = fees['transaction'] pm = self.default_pm.lower() try: pm_fee = fees['payment_methods'][pm] except KeyError: raise PaymentException("Missing fee {0}".format(pm)) if '%' in str(pm_fee): part = Decimal(pm_fee.replace('%', '')) / 100 return self.order_payment.amount.amount * part else: return pm_fee + transaction_fee
def create_payment(self): """ Create a new payment """ if self.order_payment.amount.currency != XOF: raise PaymentException( "Can only do Vitepay payments in XOF, Communauté Financière Africaine (BCEAO)." ) if self.merchant != 'vitepay': raise PaymentException( "Not a VitePay order-payment. Merchant is {0}".format( self.merchant)) payment = self.MODEL_CLASSES[0](order_payment=self.order_payment) # Amount on the payment should be in CFA * 100 payment.amount_100 = int(self.order_payment.amount.amount * 100) payment.description = "Thanks for your donation!" payment.callback_url = self._get_callback_url() payment.return_url = '{0}/orders/{1}/success'.format( get_current_host(), self.order_payment.order.id) payment.decline_url = '{0}/orders/{1}/failed'.format( get_current_host(), self.order_payment.order.id) payment.cancel_url = '{0}/orders/{1}/failed'.format( get_current_host(), self.order_payment.order.id) payment.order_id = 'opc-{0}'.format(self.order_payment.id) payment.save() return payment
def get_authorization_action(self): client = gateway.DocdataClient(self.credentials, self.live_mode) # Get the language that the user marked as his / her primary language # or fallback on the default LANGUAGE_CODE in settings try: client_language = self.order_payment.order.user.primary_language except AttributeError: client_language = properties.LANGUAGE_CODE if self.order_payment.payment_method == 'docdataDirectdebit': try: client.start_remote_payment( order_key=self.payment.payment_cluster_key, payment=self.payment, payment_method='SEPA_DIRECT_DEBIT') return {'type': 'success'} except DocdataPaymentException as i: raise PaymentException(i) else: return_url_base = get_current_host() try: url = client.get_payment_menu_url( order_key=self.payment.payment_cluster_key, credentials=self.credentials, order_id=self.order_payment.order_id, return_url=return_url_base, client_language=client_language, ) except DocdataPaymentException as i: raise PaymentException(i) default_act = False if self.payment.ideal_issuer_id: default_act = True if self.payment.default_pm == 'paypal_express_checkout': default_act = True url = client.get_payment_menu_url( order_key=self.payment.payment_cluster_key, order_id=self.order_payment.order_id, credentials=self.credentials, return_url=return_url_base, client_language=client_language, ) default_act = False if self.payment.ideal_issuer_id: default_act = True params = { 'default_pm': self.payment.default_pm, 'ideal_issuer_id': self.payment.ideal_issuer_id, 'default_act': default_act } url += '&' + urlencode(params) return {'type': 'redirect', 'method': 'get', 'url': url}
def test_check_payment_status(self): request = self.request_factory.post('/') request.user = MockUser() with mock.patch.object(DocdataPaymentAdapter, 'check_payment_status') as check_payment_status: response = self.order_payment_admin.check_status( request, self.order_payment.pk) self.assertEqual(response.status_code, 302) check_payment_status.assert_called() # Pretend the adapter changed payment status to 'settled' self.payment.status = 'settled' self.payment.save() self.assertEqual(self.order_payment.order.status, 'success') with mock.patch.object(self.order_payment_admin, 'message_user') as message_mock: with mock.patch.object( DocdataPaymentAdapter, 'check_payment_status') as check_payment_status: check_payment_status.side_effect = PaymentException( 'Docdata just exploded!') response = self.order_payment_admin.check_status( request, self.order_payment.pk) self.assertEqual(response.status_code, 302) message_mock.assert_called_with( request, 'Error checking status Docdata just exploded!', level='WARNING') check_payment_status.assert_called()
def _get_adapter(self): # FIXME: Check if payment_method is set. provider_name = re.sub('([a-z]+)([A-Z][a-z]+)', r'\1', self.order_payment.payment_method) method_name = re.sub('([a-z]+)([A-Z][a-z]+)', r'\2', self.order_payment.payment_method) app_name = 'payments_' + provider_name # First try to load the specific profile adapter class_name = provider_name.title() + method_name + 'PaymentAdapter' class_path = 'bluebottle.' + app_name + '.adapters.' + class_name try: adapter_class = get_class(class_path) except GetClassError: # Now try to load the generic provider adapter class_name = provider_name.title() + 'PaymentAdapter' class_path = 'bluebottle.' + app_name + '.adapters.' + class_name try: adapter_class = get_class(class_path) except GetClassError: raise PaymentException( "Couldn't find an adapter for payment method '{0}'".format( self.order_payment.payment_method)) adapter = adapter_class(self.order_payment) return adapter
def create_payment(self): """ Create a new payment """ payment = self.MODEL_CLASSES[0](order_payment=self.order_payment) payment.product_id = self.credentials['product_id'] payment.pay_item_id = self.credentials['item_id'] # Amount on the payment should be in kobo/cents payment.amount = int(self.order_payment.amount.amount * 100) if self.order_payment.amount.currency != NGN: raise PaymentException( "Can only do Interswitch payments in Nigerian Naira (NGN).") payment.site_redirect_url = '{0}/payments_interswitch/payment_response/{1}'.format( get_current_host(), self.order_payment.id) tenant = connection.tenant payment.site_name = str(tenant.domain_url) try: payment.cust_id = self.order_payment.user.id payment.cust_name = unicode(self.order_payment.user.full_name) except AttributeError: # Anonymous order pass payment.txn_ref = '{0}-{1}'.format(tenant.name, self.order_payment.id) payment.save() return payment
def start_payment(self): # Throw some errors op = self.order_payment if op.id % 2: raise PaymentException('The horror, the horror') else: op.started() op.settled() op.save() return True
def status_update(self, authenticity, success, failure): """ Check the received status update and update payment status accordingly. Note: We can't check status from our site. We have to rely on VitePay sending us an update. """ if authenticity != self._create_update_hash(): raise PaymentException('Authenticity incorrect.') elif success and failure: raise PaymentException( 'Both failure and succes are set. Not sure what to do.') elif not success and not failure: raise PaymentException( 'Both failure and succes are unset. Not sure what to do.') elif failure: self.payment.status = StatusDefinition.FAILED else: self.payment.status = StatusDefinition.SETTLED self.payment.save() return True
def create_payment(self): try: can_pledge = self.order_payment.user.can_pledge except AttributeError: can_pledge = False if not can_pledge: raise PaymentException('User does not have permission to pledge') # A little hacky but we can set the status to pledged here if self.order_payment.status == StatusDefinition.CREATED: self.order_payment.pledged() self.order_payment.save()
def create_payment(self): """ Create a new payment """ self.card_data = self.order_payment.card_data if 'mobile' not in self.card_data: raise PaymentException('Mobile is required') payment = self.MODEL_CLASSES[0](order_payment=self.order_payment, mobile=self.card_data['mobile']) payment.amount = int(self.order_payment.amount.amount) if str(self.order_payment.amount.currency) != 'USD': raise PaymentException( 'You should pick USD as a currency to use Telesom/Zaad') payment.currency = str(self.order_payment.amount.currency) payment.narration = "Donation {0}".format(self.order_payment.id) gateway = TelesomClient(merchant_id=self.credentials['merchant_id'], merchant_key=self.credentials['merchant_key'], username=self.credentials['username'], password=self.credentials['password'], api_domain=self.credentials['api_domain']) tenant = connection.tenant payment.description = '{0}-{1}'.format(tenant.name, self.order_payment.id) response = gateway.create(mobile=payment.mobile, amount=payment.amount, description=payment.description) payment.transaction_reference = response['payment_id'] payment.status = response['status'] payment.response = response['response'] payment.save() self.payment = payment # Check status right away so the payment gets processed self.check_payment_status() return payment
def create_payment(self): """ Have some basic criteria that might fail so we can check our error parsing. """ if self.order_payment.amount.amount < 10: raise PaymentException( "Amount for Mock payments should be greater then 10") user_data = self.get_user_data() pattern = re.compile(r'\W') if pattern.findall(user_data['first_name']): raise PaymentException( "First name '{0}' has got illegal characters.".format( user_data['first_name'])) if len(user_data['last_name']) > 30: raise PaymentException("Last name too long: '{0}'".format( user_data["last_name"])) # Now just create the payment. payment = self.MODEL_CLASSES[0](order_payment=self.order_payment) payment.save() return payment
def get_authorization_action(self): if self.payment.status == 'settled': return {'type': 'success'} if self.payment.status == 'started': return { 'type': 'process', 'payload': { 'account_number': self.payment.account_number, 'business_number': self.payment.business_number, 'amount': int(float(self.payment.amount)) } } raise PaymentException('Payment could not be verified yet.')
def create_payment(self): """ Create a new payment """ self.card_data = self.order_payment.card_data if 'mobile' not in self.card_data: raise PaymentException('Mobile is required') mobile = self.card_data['mobile'] if mobile[0:2] == '07': mobile = '+256' + mobile[1:] payment = BeyonicPayment(order_payment=self.order_payment, mobile=mobile) payment.amount = int(self.order_payment.amount.amount) payment.currency = str(self.order_payment.amount.currency) payment.metadata = {'order_id': self.order_payment.id} if not self.credentials['live']: # Testing currency payment.currency = 'BXC' tenant = connection.tenant payment.description = '{0}-{1}'.format(tenant.name, self.order_payment.id) beyonic.api_key = self.credentials['merchant_key'] callback_url = '{0}/payments_beyonic/update/'.format( get_current_host()) response = beyonic.CollectionRequest.create( phonenumber=payment.mobile, amount=payment.amount, currency=payment.currency, description=payment.description, callback_url=callback_url, metadata=payment.metadata, send_instructions=True) payment.transaction_reference = response['id'] payment.status = self.status_mapping[response['status']] payment.response = response payment.save() self.payment = payment # Check status right away so the payment gets processed self.check_payment_status() return payment
def create_payment(self): self.card_data = self.order_payment.card_data if not {'card_number', 'expiry_month', 'expiry_year', 'cvv'}.issubset( self.card_data): logger.warn('payment_tracer: {}, ' 'event: payment.flutterwave.invalid_credentials,' 'card_number: {}, ' 'expiry_month: {}, ' 'expiry_year: {}, ' 'cvv: {}'.format( self.payment_tracer, getattr(self.card_data, 'card_number', None), getattr(self.card_data, 'expiry_month', None), getattr(self.card_data, 'expiry_year', None), getattr(self.card_data, 'cvv', None))) raise PaymentException( 'Card number, expiry month/year and cvv is required') payment = FlutterwavePayment(order_payment=self.order_payment, card_number="**** **** **** " + self.card_data['card_number'][-4:]) if 'pin' in self.card_data and self.card_data['pin']: payment.auth_model = 'PIN' else: payment.auth_model = 'VBVSECURECODE' payment.amount = str(self.order_payment.amount.amount) payment.currency = str(self.order_payment.amount.currency) payment.customer_id = str(self.order_payment.user or 1) payment.narration = "Donation {0}".format(self.order_payment.id) payment.response_url = '{0}/payments_flutterwave/payment_response/{1}'.format( get_current_host(), self.order_payment.id) tenant = connection.tenant payment.site_name = str(tenant.domain_url) try: payment.cust_id = self.order_payment.user.id payment.cust_name = unicode(self.order_payment.user.full_name) except AttributeError: # Anonymous order pass payment.txn_ref = '{0}-{1}'.format(tenant.name, self.order_payment.id) payment.save() self.payment_logger.log( payment, 'info', 'payment_tracer: {}, ' 'event: payment.flutterwave.create_payment.success'.format( self.payment_tracer)) return payment
def create_account_number(self, project): client = self._get_client() response = client.create_payment_account( transaction_account_type=1, transaction_account_name=project.slug, transaction_account_manager=self.credentials['channel_manager'] ) try: account_number = response['content']['transaction_account_number'] LipishaProject.objects.create( project=project, account_number=account_number ) except KeyError: raise PaymentException("Could not create an account number at Lipisha")
def get_authorization_action(self): """ Handle payment """ if self.payment.status == 'settled': return {'type': 'success'} elif self.payment.status == 'started': return { 'type': 'step2', 'payload': { 'method': 'telesom-sms', 'text': 'Confirm the payment by SMS' } } else: reply = self.payment.update_response raise PaymentException( "Error processing Telesom/Zaad transaction. {0}".format(reply))
def check_payment_status(self): transaction_reference = self.payment.transaction_reference card_data = self.order_payment.card_data or {} if 'otp' in card_data: otp = card_data['otp'] data = { "otp": otp, "otpTransactionIdentifier": self.payment.transaction_reference, "country": "NG" } logger.info( 'payment_tracer: {}, ' 'event: payment.flutterwave.payment_status.otp_validate.request, ' 'flutterwave_request: {}'.format(self.payment_tracer, data)) r = self.flw.card.validate(data) response = json.loads(r.text) if response['data']['responsecode'] in SUCCESS_RESPONSECODES: self.order_payment.set_authorization_action( {'type': 'success'}) self.payment.status = 'settled' else: self.payment.status = 'failed' else: r = self.flw.card.verifyCharge( transactionRef=transaction_reference, country='NG') response = json.loads(r.text) if response['data']['responsecode'] in SUCCESS_RESPONSECODES: self.payment.status = 'settled' else: self.payment.status = 'failed' logger.info('payment_tracer: {}, ' 'transaction_reference: {}, ' 'event: payment.flutterwave.payment_status, ' 'flutterwave_response: {}'.format(self.payment_tracer, transaction_reference, response['data'])) self.payment.update_response = response self.payment.save() if self.payment.status == 'failed': raise PaymentException(response['data']['responsemessage'])
def check_payment_status(self): # If we have a transaction reference, then use that if self.payment.transaction_reference and self.payment.transaction_reference != '4': response = self.client.get_transactions( transaction_type='Payment', transaction=self.payment.transaction_reference ) else: response = self.client.get_transactions( transaction_type='Payment', transaction_reference=self.order_payment.id ) self.payment.update_response = json.dumps(response) data = response['content'] if len(data) == 0: self.payment.status = StatusDefinition.FAILED self.payment.save() raise PaymentException('Payment could not be verified yet. Payment not found.') else: payment = data[0] # Make sure we set the right properties payment['transaction_reference'] = payment['transaction'] for k, v in payment.iteritems(): setattr(self.payment, k, v) if self.payment.transaction_amount != self.payment.order_payment.amount.amount: # Update donation amount based on the amount registered at Lipisha amount = Money(self.payment.transaction_amount, 'KES') donation = self.payment.order_payment.order.donations.all()[0] self.payment.order_payment.amount = amount donation.amount = amount donation.save() self.payment.order_payment.save() self.payment.status = self._get_mapped_status(self.payment.transaction_status) if self.payment.status in ['settled', 'authorized']: self.order_payment.set_authorization_action({'type': 'success'}) self.payment.save()
def get_authorization_action(self): """ Handle payment """ if self.payment.status == 'settled': return {'type': 'success'} elif self.payment.status == 'started': return { 'type': 'step2', 'payload': { 'method': 'beyonic-sms', 'text': 'Confirm the payment on your mobile' } } else: try: reply = self.payment.update_response['error_message'] except KeyError: reply = self.payment.update_response raise PaymentException( "Error processing AirTel/MTN transaction. {0}".format(reply))
def _get_payment_url(self): """ Get payment url from VitePay to redirect the user to. """ data = { "payment": { "language_code": "fr", "currency_code": "XOF", "country_code": "ML", "order_id": self.payment.order_id, "description": self.payment.description, "amount_100": self.payment.amount_100, "return_url": self.payment.return_url, "decline_url": self.payment.decline_url, "cancel_url": self.payment.cancel_url, "callback_url": self.payment.callback_url, "p_type": "orange_money", }, "redirect": 0, "api_key": self.credentials['api_key'], "hash": self._create_payment_hash() } url = self.credentials['api_url'] headers = {'Content-Type': 'application/json'} response = requests.post(url, data=json.dumps(data), headers=headers, verify=False) if response.status_code == 200: self.payment.payment_url = response.content else: raise PaymentException('Error creating payment: {0}'.format( response.content)) self.payment.status = StatusDefinition.STARTED self.payment.save() return response.content
def create(self, mobile='', amount=0, description=''): """ Create the payment in Telesom. """ # We should not use actual IP address. ip = '::1' date = timezone.now().strftime('%d/%m/%Y') username = self.username password = self.password account = self.merchant_id unique_key = self.merchant_key # From PHP: # $msg = $username.$password."::1".$merchant.$uniquekey. $dates.$mobile.$amount.$description; hash = "{0}{1}{2}{3}{4}{5}{6}{7}{8}".format(username, password, ip, account, unique_key, date, mobile, amount, description) key = hashlib.md5(hash).hexdigest() if self.testing: reply = self.client.service.PaymentRequest( pMsisdn=mobile, pAmount=amount, Category=description, MerchantID=self.merchant_id, hashkey=key) else: reply = self.client.service.PaymentRequest( Subscriber=mobile, Amount=amount, Account=self.merchant_id, Description=description, Key=key) # Requests (including typos, please leave as is) # ------------------ # 2001! Success, Waiting Confirmation !100 # 400!Error: connect ECONNREFUSED! # 400!Error: connect Unknown system errno 10056! # 4005! Insufficient Payer Account funds.! # 4005! Invalid Payee Account Info. ! # 4005! Invalid Payee Account relation. ! # 4005! OOPS ! , Sorry your request could not be complete this time !! # 4005! There is somthing got wrong, please try again ! # 4005!! # 4005!Payer Account Does not Exist.! # 5000!Account Locked, You can Try again after 24 Hours!-1 # 5001! Invalid Username/Password/Hashkey Try Again!-1 # 5002!Error Occured Cannot Proccess Payment!-1 # 5004!Authentication Error!-1 res = reply.split('!') if res[0] == '2001': return { 'response': reply, 'status': 'created', 'payment_id': res[2] } else: raise PaymentException( "Could not start Telesom/Zaad transaction. {0}".format(reply))
def get_fee(self): if not isinstance(self, Payment): raise PaymentException("get_fee() not implemented for " "{0}".format(self.__class__.__name__))
def get_authorization_action(self): pin = '' cvv = '' if 'pin' in self.card_data: pin = self.card_data['pin'] if 'cvv' in self.card_data: cvv = self.card_data['cvv'] if not {'card_number', 'expiry_month', 'expiry_year', 'cvv'}.issubset( self.card_data): logger.warn('payment_tracer: {}, ' 'event: payment.flutterwave.invalid_credentials,' 'card_number: {}, ' 'expiry_month: {}, ' 'expiry_year: {}, ' 'cvv: {}'.format( self.payment_tracer, getattr(self.card_data, 'card_number', None), getattr(self.card_data, 'expiry_month', None), getattr(self.card_data, 'expiry_year', None), getattr(self.card_data, 'cvv', None))) raise PaymentException( 'Card number, expiry month/year and cvv is required') data = { "amount": self.payment.amount, "currency": self.payment.currency, "authModel": self.payment.auth_model, "cardNumber": self.card_data['card_number'], "cvv": cvv, "expiryMonth": self.card_data['expiry_month'], "expiryYear": self.card_data['expiry_year'], "pin": pin, "customerID": self.payment.customer_id, "narration": self.payment.narration, "responseUrl": self.payment.response_url, "country": self.payment.country } logger.info( 'payment_tracer: {}, ' 'event: payment.flutterwave.get_authorization_action.request' 'amount: {}, ' 'currency: {}, ' 'authModel: {}, ' 'cardNumber: {}, ' 'cvv: {}, ' 'expiryMonth: {}, ' 'expiryYear: {}, ' 'pin: {}, ' 'customerId: {}, ' 'narration: {}, ' 'responseUrl: {}, ' 'country: {}'.format( self.payment_tracer, self.payment.amount, self.payment.currency, self.payment.auth_model, self.card_data['card_number'][-4:], cvv, self.card_data['expiry_month'], self.card_data['expiry_year'], pin, self.payment.customer_id, self.payment.narration, self.payment.response_url, self.payment.country)) r = self.flw.card.charge(data) if r.status_code == 500: logger.warn('payment_tracer: {}, ' 'event: payment.flutterwave.error.500, ' 'flutterwave_response: {}'.format( self.payment_tracer, r.text)) raise PaymentException( 'Flutterwave could not confirm your card details, please try again.' ) response = json.loads(r.text) self.payment.response = "{}".format(r.text) self.payment.save() logger.info( 'payment_tracer: {}, ' 'event: payment.flutterwave.get_authorization_action.response, ' 'flutterwave_response: {}'.format(self.payment_tracer, r.text)) if response['status'] == u'error': logger.warn( 'payment_tracer: {}, ' 'event: payment.flutterwave.get_authorization_action.error, ' 'flutterwave_response: {}'.format(self.payment_tracer, response['data'])) raise PaymentException('Flutterwave error: {0}'.format( response['data'])) if response['data']['responsecode'] in SUCCESS_RESPONSECODES: self.payment.status = 'authorized' self.payment.save() logger.info( 'payment_tracer: {}, ' 'event: payment.flutterwave.get_authorization_action.authorized, ' 'response: {}'.format(self.payment_tracer, response['data'])) return {'type': 'success'} if response['data']['responsecode'] == '02': if 'authurl' in response['data'] and response['data']['authurl']: return { 'method': 'get', 'url': response['data']['authurl'], 'type': 'redirect', 'payload': { 'method': 'flutterwave-otp', 'text': response['data']['responsemessage'], } } else: return { 'type': 'step2', 'payload': { 'method': 'flutterwave-otp', 'text': response['data']['responsemessage'] } } logger.warn( 'payment_tracer: {}, ' 'event: payment.flutterwave.get_authorization_action.error.start_payment ' 'flutterwave_response: {}'.format(self.payment_tracer, response['data'])) raise PaymentException('Error starting payment: {0}'.format( response['data']['responsemessage']))