def test_truncation(self): """Verify that the truncation utility behaves as expected.""" length = 10 string = 'x' * length # Verify that the original string is returned when no truncation is necessary. self.assertEqual(string, middle_truncate(string, length)) self.assertEqual(string, middle_truncate(string, length + 1)) # Verify that truncation occurs when expected. self.assertEqual('xxx...xxx', middle_truncate(string, length - 1)) self.assertEqual('xx...xx', middle_truncate(string, length - 2)) self.assertRaises(ValueError, middle_truncate, string, 0)
def get_transaction_parameters(self, basket, request=None): """ Create a new PayPal payment. Arguments: basket (Basket): The basket of products being purchased. Keyword Arguments: request (Request): A Request object which is used to construct PayPal's `return_url`. Returns: dict: PayPal-specific parameters required to complete a transaction. Must contain a URL to which users can be directed in order to approve a newly created payment. Raises: GatewayError: Indicates a general error or unexpected behavior on the part of PayPal which prevented a payment from being created. """ return_url = urljoin(self.ecommerce_url_root, reverse('paypal_execute')) data = { 'intent': 'sale', 'redirect_urls': { 'return_url': return_url, 'cancel_url': self.cancel_url, }, 'payer': { 'payment_method': 'paypal', }, 'transactions': [{ 'amount': { 'total': unicode(basket.total_incl_tax), 'currency': basket.currency, }, 'item_list': { 'items': [ { 'quantity': line.quantity, # PayPal requires that item names be at most 127 characters long. 'name': middle_truncate(line.product.title, 127), 'price': unicode(line.price_incl_tax), 'currency': line.stockrecord.price_currency, } for line in basket.all_lines() ], }, 'invoice_number': basket.order_number, }], } try: web_profile = PaypalWebProfile.objects.get(name=self.DEFAULT_PROFILE_NAME) data['experience_profile_id'] = web_profile.id except PaypalWebProfile.DoesNotExist: pass payment = paypalrestsdk.Payment(data) payment.create() # Raise an exception for payments that were not successfully created. Consuming code is # responsible for handling the exception. if not payment.success(): error = self._get_error(payment) entry = self.record_processor_response(error, transaction_id=error['debug_id'], basket=basket) logger.error( u"Failed to create PayPal payment for basket [%d]. PayPal's response was recorded in entry [%d].", basket.id, entry.id ) raise GatewayError(error) entry = self.record_processor_response(payment.to_dict(), transaction_id=payment.id, basket=basket) logger.info(u"Successfully created PayPal payment [%s] for basket [%d].", payment.id, basket.id) for link in payment.links: if link.rel == 'approval_url': approval_url = link.href break else: logger.error( u"Approval URL missing from PayPal payment [%s]. PayPal's response was recorded in entry [%d].", payment.id, entry.id ) raise GatewayError( 'Approval URL missing from PayPal payment response. See entry [{}] for details.'.format(entry.id)) parameters = { 'payment_page_url': approval_url, } return parameters
def get_transaction_parameters(self, basket, request=None, use_client_side_checkout=False, **kwargs): """ Create a new PayPal payment. Arguments: basket (Basket): The basket of products being purchased. request (Request, optional): A Request object which is used to construct PayPal's `return_url`. use_client_side_checkout (bool, optional): This value is not used. **kwargs: Additional parameters; not used by this method. Returns: dict: PayPal-specific parameters required to complete a transaction. Must contain a URL to which users can be directed in order to approve a newly created payment. Raises: GatewayError: Indicates a general error or unexpected behavior on the part of PayPal which prevented a payment from being created. """ # PayPal requires that item names be at most 127 characters long. PAYPAL_FREE_FORM_FIELD_MAX_SIZE = 127 return_url = urljoin(get_ecommerce_url(), reverse('paypal:execute')) data = { 'intent': 'sale', 'redirect_urls': { 'return_url': return_url, 'cancel_url': self.cancel_url, }, 'payer': { 'payment_method': 'paypal', }, 'transactions': [{ 'amount': { 'total': str(basket.total_incl_tax), 'currency': basket.currency, }, # Paypal allows us to send additional transaction related data in 'description' & 'custom' field # Free form field, max length 127 characters # description : program_id:<program_id> 'description': "program_id:{}".format(get_basket_program_uuid(basket)), 'item_list': { 'items': [ { 'quantity': line.quantity, # PayPal requires that item names be at most 127 characters long. # for courseid we're using 'name' field along with title, # concatenated field will be 'courseid|title' 'name': middle_truncate(self.get_courseid_title(line), PAYPAL_FREE_FORM_FIELD_MAX_SIZE), # PayPal requires that the sum of all the item prices (where price = price * quantity) # equals to the total amount set in amount['total']. 'price': str(line.line_price_incl_tax_incl_discounts / line.quantity), 'currency': line.stockrecord.price_currency, } for line in basket.all_lines() ], }, 'invoice_number': basket.order_number, }], } if waffle.switch_is_active('create_and_set_webprofile'): locale_code = self.resolve_paypal_locale( request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME)) web_profile_id = self.create_temporary_web_profile(locale_code) if web_profile_id is not None: data['experience_profile_id'] = web_profile_id else: try: web_profile = PaypalWebProfile.objects.get( name=self.DEFAULT_PROFILE_NAME) data['experience_profile_id'] = web_profile.id except PaypalWebProfile.DoesNotExist: pass available_attempts = 1 if waffle.switch_is_active('PAYPAL_RETRY_ATTEMPTS'): available_attempts = self.retry_attempts for i in range(1, available_attempts + 1): try: payment = paypalrestsdk.Payment(data, api=self.paypal_api) payment.create() if payment.success(): break if i < available_attempts: logger.warning( u"Creating PayPal payment for basket [%d] was unsuccessful. Will retry.", basket.id, exc_info=True) else: error = self._get_error(payment) # pylint: disable=unsubscriptable-object entry = self.record_processor_response( error, transaction_id=error['debug_id'], basket=basket) logger.error(u"%s [%d], %s [%d].", "Failed to create PayPal payment for basket", basket.id, "PayPal's response recorded in entry", entry.id, exc_info=True) raise GatewayError(error) except: # pylint: disable=bare-except if i < available_attempts: logger.warning( u"Creating PayPal payment for basket [%d] resulted in an exception. Will retry.", basket.id, exc_info=True) else: logger.exception( u"After %d retries, creating PayPal payment for basket [%d] still experienced exception.", i, basket.id) raise entry = self.record_processor_response(payment.to_dict(), transaction_id=payment.id, basket=basket) logger.info( "Successfully created PayPal payment [%s] for basket [%d].", payment.id, basket.id) for link in payment.links: if link.rel == 'approval_url': approval_url = link.href break else: logger.error( "Approval URL missing from PayPal payment [%s]. PayPal's response was recorded in entry [%d].", payment.id, entry.id) raise GatewayError( 'Approval URL missing from PayPal payment response. See entry [{}] for details.' .format(entry.id)) parameters = { 'payment_page_url': approval_url, } return parameters
def get_transaction_parameters(self, basket, request=None): """ Create a new PayPal payment. Arguments: basket (Basket): The basket of products being purchased. Keyword Arguments: request (Request): A Request object which is used to construct PayPal's `return_url`. Returns: dict: PayPal-specific parameters required to complete a transaction. Must contain a URL to which users can be directed in order to approve a newly created payment. Raises: GatewayError: Indicates a general error or unexpected behavior on the part of PayPal which prevented a payment from being created. """ return_url = urljoin(get_ecommerce_url(), reverse('paypal_execute')) data = { 'intent': 'sale', 'redirect_urls': { 'return_url': return_url, 'cancel_url': self.cancel_url, }, 'payer': { 'payment_method': 'paypal', }, 'transactions': [{ 'amount': { 'total': unicode(basket.total_incl_tax), 'currency': basket.currency, }, 'item_list': { 'items': [ { 'quantity': line.quantity, # PayPal requires that item names be at most 127 characters long. 'name': middle_truncate(line.product.title, 127), # PayPal requires that the sum of all the item prices (where price = price * quantity) # equals to the total amount set in amount['total']. 'price': unicode(line.line_price_incl_tax_incl_discounts / line.quantity), 'currency': line.stockrecord.price_currency, } for line in basket.all_lines() ], }, 'invoice_number': basket.order_number, }], } try: web_profile = PaypalWebProfile.objects.get(name=self.DEFAULT_PROFILE_NAME) data['experience_profile_id'] = web_profile.id except PaypalWebProfile.DoesNotExist: pass payment = paypalrestsdk.Payment(data, api=self.paypal_api) payment.create() # Raise an exception for payments that were not successfully created. Consuming code is # responsible for handling the exception. if not payment.success(): error = self._get_error(payment) entry = self.record_processor_response(error, transaction_id=error['debug_id'], basket=basket) # pylint: disable=unsubscriptable-object logger.error( u"Failed to create PayPal payment for basket [%d]. PayPal's response was recorded in entry [%d].", basket.id, entry.id ) raise GatewayError(error) entry = self.record_processor_response(payment.to_dict(), transaction_id=payment.id, basket=basket) logger.info(u"Successfully created PayPal payment [%s] for basket [%d].", payment.id, basket.id) for link in payment.links: if link.rel == 'approval_url': approval_url = link.href break else: logger.error( u"Approval URL missing from PayPal payment [%s]. PayPal's response was recorded in entry [%d].", payment.id, entry.id ) raise GatewayError( 'Approval URL missing from PayPal payment response. See entry [{}] for details.'.format(entry.id)) parameters = { 'payment_page_url': approval_url, } return parameters
def get_transaction_parameters(self, basket, request=None, use_client_side_checkout=False, **kwargs): """ Create a new Alipay payment. Arguments: basket (Basket): The basket of products being purchased. request (Request, optional): A Request object which is used to construct PayPal's `return_url`. use_client_side_checkout (bool, optional): This value is not used. **kwargs: Additional parameters; not used by this method. Returns: dict: PayPal-specific parameters required to complete a transaction. Must contain a URL to which users can be directed in order to approve a newly created payment. Raises: GatewayError: Indicates a general error or unexpected behavior on the part of PayPal which prevented a payment from being created. """ return_url = urljoin(get_ecommerce_url(), reverse('alipay:execute')) data = { 'intent': 'sale', 'redirect_urls': { 'return_url': return_url, 'cancel_url': self.cancel_url, }, 'payer': { 'payment_method': 'alipay', }, 'transactions': [{ 'amount': { 'total': unicode(basket.total_incl_tax), 'currency': basket.currency, }, 'item_list': { 'items': [ { 'quantity': line.quantity, # PayPal requires that item names be at most 127 characters long. 'name': middle_truncate(line.product.title, 127), # PayPal requires that the sum of all the item prices (where price = price * quantity) # equals to the total amount set in amount['total']. 'price': unicode(line.line_price_incl_tax_incl_discounts / line.quantity), 'currency': line.stockrecord.price_currency, } for line in basket.all_lines() ], }, 'invoice_number': basket.order_number, }], } if waffle.switch_is_active('create_and_set_webprofile'): locale_code = self.resolve_alipay_locale( request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME)) web_profile_id = self.create_temporary_web_profile(locale_code) if web_profile_id is not None: data['experience_profile_id'] = web_profile_id else: try: web_profile = AlipayWebProfile.objects.get( name=self.DEFAULT_PROFILE_NAME) data['experience_profile_id'] = web_profile.id except AlipayWebProfile.DoesNotExist: pass available_attempts = 1 if waffle.switch_is_active('PAYPAL_RETRY_ATTEMPTS'): available_attempts = self.retry_attempts for i in range(1, available_attempts + 1): try: payment = alipay_sdk.Payment(data, api=self.alipay_api) payment.create() if payment.success(): break else: if i < available_attempts: logger.warning( u"Creating AliPay payment for basket [%d] was unsuccessful. Will retry.", basket.id, exc_info=True) else: error = self._get_error(payment) # pylint: disable=unsubscriptable-object entry = self.record_processor_response( error, transaction_id=error['debug_id'], basket=basket) logger.error( u"%s [%d], %s [%d].", "Failed to create AliPay payment for basket", basket.id, "AliPay's response recorded in entry", entry.id, exc_info=True) raise GatewayError(error) except: # pylint: disable=bare-except if i < available_attempts: logger.warning( u"Creating AliPay payment for basket [%d] resulted in an exception. Will retry.", basket.id, exc_info=True) else: logger.exception( u"After %d retries, creating AliPay payment for basket [%d] still experienced exception.", i, basket.id) raise entry = self.record_processor_response(payment.to_dict(), transaction_id=payment.id, basket=basket) logger.info( "Successfully created AliPay payment [%s] for basket [%d].", payment.id, basket.id) id = payment.id order_string = self.alipay_api.api_alipay_trade_page_pay( out_trade_no=payment.id, total_amount=unicode(basket.total_incl_tax), #0.01, subject=middle_truncate(line.product.title, 127), return_url=return_url, ) parameters = { 'payment_page_url': self.alipay_api.default_endpoint() + '?' + order_string, #'payment_page_url': 'error_test', } return parameters