def test_calculates_amount_for_refund_correctly(self): source = Source( amount_allocated=D('100'), amount_debited=D('80'), amount_refunded=D('20')) self.assertEqual( D('80') - D('20'), source.amount_available_for_refund)
def test_calculates_balance_correctly(self): source = Source( amount_allocated=D('100'), amount_debited=D('80'), amount_refunded=D('20')) self.assertEqual( D('100') - D('80') + D('20'), source.balance)
def handle_payment(self, order_number, total, **kwargs): # Make request to DataCash - if there any problems (eg bankcard # not valid / request refused by bank) then an exception would be # raised and handled) facade = Facade() # Use The3rdMan - so build a dict of data to pass # email = None # if not self.request.user.is_authenticated(): # email = self.checkout_session.get_guest_email() # fraud_data = the3rdman.build_data_dict( # request=self.request, # email=email, # order_number=order_number, # shipping_address=kwargs['shipping_address']) # We're not using 3rd-man by default bankcard = kwargs['bankcard_form'].bankcard datacash_ref = facade.pre_authorise(order_number, total.incl_tax, bankcard) # Request was successful - record the "payment source". As this # request was a 'pre-auth', we set the 'amount_allocated' - if we had # performed an 'auth' request, then we would set 'amount_debited'. source_type, _ = SourceType.objects.get_or_create(name='Datacash') source = Source(source_type=source_type, currency=settings.DATACASH_CURRENCY, amount_allocated=total.incl_tax, reference=datacash_ref) self.add_payment_source(source) # Also record payment event self.add_payment_event('pre-auth', total.incl_tax)
class ConfirmView(OrderPlacementMixin, View): """ Handle the response from GoCardless """ def get(self, request, *args, **kwargs): try: facade.confirm(request) except PaymentError, e: messages.error(self.request, str(e)) self.restore_frozen_basket() return HttpResponseRedirect(reverse('checkout:payment-details')) # Fetch submission data out of session order_number = self.checkout_session.get_order_number() basket = self.get_submitted_basket() total_incl_tax, total_excl_tax = self.get_order_totals(basket) # Record payment source source_type, is_created = SourceType.objects.get_or_create( name='GoCardless') source = Source(source_type=source_type, currency='GBP', amount_allocated=total_incl_tax, amount_debited=total_incl_tax) self.add_payment_source(source) # Place order return self.handle_order_placement(order_number, basket, total_incl_tax, total_excl_tax)
def handle_payment(self, order_number, total, **kwargs): transaction_id = kwargs.get('transaction_id', None) if transaction_id: uplustransaction = UplusTransaction.objects.get(id=transaction_id) uplustransaction.order_number=order_number uplustransaction.save() basket = self.load_frozen_basket(uplustransaction.basket_id) basket.submit() else: raise UplusError(u'Transaction id missing.') pay_type_to_string = dict(UplusPayTypeForm.UPLUS_PAY_TYPE)[uplustransaction.pay_type] source_type, is_created = SourceType.objects.get_or_create( name='Uplus '+ pay_type_to_string) source = Source(source_type=source_type, currency=total.currency, amount_allocated=total.incl_tax, ) self.add_payment_source(source) if uplustransaction.pay_type == 'SC0040': self.add_payment_event('Pending', total.incl_tax, reference=order_number) else: self.add_payment_event('Complete', total.incl_tax, reference=order_number)
def handle_payment(self, order_number, total_incl_tax, **kwargs): """ Complete payment with PayPal - this calls the 'DoExpressCheckout' method to capture the money from the initial transaction. """ try: payer_id = self.request.POST['payer_id'] token = self.request.POST['token'] except KeyError: raise PaymentError( "Unable to determine PayPal transaction details") try: txn = confirm_transaction(payer_id, token, amount=self.txn.amount, currency=self.txn.currency) except PayPalError: raise UnableToTakePayment() if not txn.is_successful: raise UnableToTakePayment() # Record payment source and event source_type, is_created = SourceType.objects.get_or_create( name='PayPal') source = Source(source_type=source_type, currency=txn.currency, amount_allocated=txn.amount, amount_debited=txn.amount) self.add_payment_source(source) self.add_payment_event('Settled', txn.amount)
def handle_payment(self, order_number, total, basket_lines, **kwargs): card_token = self.request.POST.get('card_token') if card_token.startswith('card_'): stripe_ref = Facade().charge( order_number, total, card=card_token, description=self.payment_description(order_number, total, **kwargs), metadata=self.payment_metadata(order_number, total, basket_lines, **kwargs), customer=self.request.user.customer.stripe_id) else: stripe_ref = Facade().charge( order_number, total, card=card_token, description=self.payment_description(order_number, total, **kwargs), metadata=self.payment_metadata(order_number, total, basket_lines, **kwargs)) source_type, __ = SourceType.objects.get_or_create(name='Credit Card') source = Source(source_type=source_type, currency=settings.STRIPE_CURRENCY, amount_allocated=total.incl_tax, amount_debited=total.incl_tax, reference=stripe_ref) self.add_payment_source(source) self.add_payment_event('Purchase', total.incl_tax)
def handle_payment(self, order_number, total, **kwargs): source_type, is_created = SourceType.objects.get_or_create( name='Espace ou ticket resto lors de la livraison') source = Source(source_type=source_type, currency=total.currency, amount_allocated=total.incl_tax, amount_debited=total.incl_tax) self.add_payment_source(source)
def handle_payment(self, order_number, total, **kwargs): submission = kwargs['submission'] # Make request to Docdata. # Any raised exceptions are handled by the PaymentDetail.submit() code. facade = CustomDocdataFacade() docdata_ref = facade.create_payment( order_number=order_number, total=total, user=submission['user'], # Extra parameters to add the "Invoice" element in Docdata: billing_address=submission[ 'shipping_address'], # NOTE: no billing address collected in steps. shipping_address=submission['shipping_address'], basket=submission['basket'], description='') # NOTE: at this point, the payment is registered as the gateway, # and there is no way back. Any errors after this part require manual intervention! # Request was successful - record the "payment source". # This represents the origin where the payment should come from. # When an order is paid in multiple parts, multiple Source objects should be created. # As this request was a 'pre-auth', we set the 'amount_allocated'. # If we had performed an 'auth' request, then we would set 'amount_debited'. source = Source( source_type=facade.get_source_type(), currency=total.currency, amount_allocated=total. incl_tax, # amount_* field depends on type of transaction. reference=docdata_ref) self.add_payment_source(source) # Also record payment event. # This will be visible in the Dashboard self.add_payment_event('pre-auth', total.incl_tax, reference=docdata_ref) # Ask oscar to redirect to docdata # TODO: test default_act="yes", skips menu entirely # TODO: add issuer_id for iDEAL. payment_url_args = {} if self.checkout_session.payment_method() is not None: payment_url_args[ 'default_pm'] = self.checkout_session.payment_method() url = facade.get_payment_menu_url(self.request, docdata_ref, **payment_url_args) logger.info("Redirecting user to {0}".format(url)) # Regardless of whether the order is paid, write it in the database before redirecting. # Oscar actually skips this when redirecting the user to the payment provider. self._save_order(order_number, submission) # Redirect the user to the payment provider. raise RedirectRequired(url)
def handle_payment(self, order_number, total, **kwargs): reference = gateway.create_transaction(order_number, total) source_type, is_created = SourceType.objects.get_or_create( name='Cash on Delivery') source = Source(source_type=source_type, currency=total.currency, amount_allocated=total.incl_tax, amount_debited=total.incl_tax) self.add_payment_source(source) self.add_payment_event('Issued', total.incl_tax, reference=reference)
def handle_payment(self, order_number, total, basket_lines, shipping_charge=0.00, **kwargs): basket_line = basket_lines.first() basket = basket_line.basket self.card_token = self.request.POST.get('card_token') payment_method = kwargs.get('payment_method') currency = total.currency if self.card_token: self.total = total self.payment_id = self.handle_stripe_payment( order_number, basket_lines, **kwargs) source_name = 'Stripe Credit Card' source_type, __ = SourceType.objects.get_or_create( name=source_name) source = Source(source_type=source_type, currency=currency, amount_allocated=total.incl_tax, amount_debited=total.incl_tax, reference=self.payment_id) self.add_payment_source(source) self.add_payment_event('Purchase', total.incl_tax, reference=self.payment_id) # Set an ongoing donation, finished when payment is confirmed self.total_deductable = basket._get_deductable_physical_total() venue = self.request.basket.get_tickets_venue() # Do not register a donation if the Venue is not part of the foundation. if venue and not venue.foundation: return # Anonymous donation or purchase if not self.request.user.is_authenticated(): return elif payment_method == 'paypal': item_list = [] # self.get_item_list(basket_lines) total_deductable = basket._get_deductable_physical_total() self.amount = str(total.incl_tax) # Donation will be set to True if user is selecting gifts # For Tickets and other goods, there will be no donation. # 'handle_paypal_payment' returns a RedirectRequiredException # and the flow will be completed in ExecutePaypalPayment self.handle_paypal_payment( currency, item_list, donation=bool(not basket_lines.first().basket.has_tickets()), deductable_total=total_deductable, shipping_charge=shipping_charge)
def get(self, request, *args, **kwargs): paypal_order_id = self.request.GET['token'] pp_response = CapturePaypalOrder().capture(paypal_order_id) order_number = kwargs.get('orderno') # get the frozen basket basket = self.get_submitted_basket() # Basket strategy needed to calculate prices # and the basket in session is stored without strategy # Use same strategy as current request basket, which # was created by the middleware basket.strategy = self.request.basket.strategy shipping_address = self.get_shipping_address(basket) shipping_method = self.get_shipping_method(basket, shipping_address) if not shipping_method: # this should be impossible at this point # however we handle it total = shipping_charge = None raise Exception( "shipping_method not set. Can't calculate order totals") else: shipping_charge = shipping_method.calculate(basket) order_total = self.get_order_totals( basket, shipping_charge=shipping_charge) # we should have exact one purchase_units and one capture capture = pp_response.result.purchase_units[0].payments.captures[0] # check if paypal payment is completed and if the paypal payment matches the order total # this case should be impossible. However is a extra security check if capture.status != "COMPLETED": msg = "Paypal Capture STATUS in not COMPLETED" logger.error(msg) return self.cancel_transaction(msg) if Decimal(capture.amount.value) != order_total.incl_tax: msg = "Paypal amount differ from order_total" logger.error(msg) return self.cancel_transaction(msg) # Payment now successful! Record payment source source_type, __ = SourceType.objects.get_or_create(name="Paypal") source = Source(source_type=source_type, amount_allocated=order_total.incl_tax, reference=pp_response.result.id) self.add_payment_source(source) # Record payment event self.add_payment_event('pre-auth', order_total.incl_tax) return self.submit(order_number, order_total, basket, shipping_address, shipping_method, shipping_charge)
def handle_payment(self, order_number, total, **kwargs): bank_account = kwargs['bank_account'] reference = gateway.create_transaction(order_number, total, bank_account) source_type, is_created = SourceType.objects.get_or_create( name=_('Bank Transfer')) source = Source(source_type=source_type, currency=total.currency, amount_allocated=total.incl_tax, amount_debited=0) self.add_payment_source(source) self.add_payment_event('Issued', total.incl_tax, reference=reference)
def handle_payment(self, order_number, total_incl_tax, **kwargs): attempt = self.capture_cielo_payment(order_number, total_incl_tax) transaction_id = attempt.transaction_id source_type, _ = SourceType.objects.get_or_create(name='Cielo') source = Source( source_type=source_type, currency='BRL', amount_allocated=total_incl_tax, amount_debited=total_incl_tax, reference=transaction_id, ) self.add_payment_source(source)
def handle_payment(self, order_number, total, **kwargs): # Override payment method to use accounts to pay for the order allocations = self.get_account_allocations() if allocations.total != total: raise exceptions.UnableToTakePayment( "Your account allocations do not cover the order total") gateway.redeem(order_number, self.request.user, allocations) # If we get here, payment was successful. We record the payment # sources and event to complete the audit trail for this order source_type, __ = SourceType.objects.get_or_create( name="Account") for code, amount in allocations.items(): source = Source( source_type=source_type, amount_debited=amount, reference=code) self.add_payment_source(source) self.add_payment_event("Settle", total)
def handle_payment(self, order_number, total_incl_tax, **kwargs): # Make request to DataCash - if there any problems (eg bankcard # not valid / request refused by bank) then an exception would be # raised ahd handled) facade = Facade() datacash_ref = facade.pre_authorise(order_number, total_incl_tax, kwargs['bankcard']) # Request was successful - record the "payment source". As this # request was a 'pre-auth', we set the 'amount_allocated' - if we had # performed an 'auth' request, then we would set 'amount_debited'. source_type, _ = SourceType.objects.get_or_create(name='Datacash') source = Source(source_type=source_type, currency=settings.DATACASH_CURRENCY, amount_allocated=total_incl_tax, reference=datacash_ref) self.add_payment_source(source) # Also record payment event self.add_payment_event('pre-auth', total_incl_tax)
def handle_payment(self, order_number, total, **kwargs): # Override payment method to use accounts to pay for the order allocations = self.get_account_allocations() if allocations.total != total.incl_tax: raise exceptions.UnableToTakePayment( "SWAMAHANI, Vos moyens de paiement sont insuffisant") try: gateway.redeem(order_number, self.request.user, allocations) except act_exceptions.AccountException: raise exceptions.UnableToTakePayment( "Il y a une erreur, reesayer plus tard") # If we get here, payment was successful. We record the payment # sources and event to complete the audit trail for this order source_type, __ = SourceType.objects.get_or_create(name="Account") for code, amount in allocations.items(): source = Source(source_type=source_type, amount_debited=amount, reference=code) self.add_payment_source(source) self.add_payment_event("Settle", total.incl_tax)
def handle_payment(self, order_number, total, **kwargs): submission = kwargs['submission'] # Create new Mollie Payment! facade = Facade() payment_id = facade.create_payment(order_number=order_number, total=total.incl_tax, redirect_url=self.get_success_url()) # Register the Oscar Source(Type) source = Source(source_type=facade.get_source_type(), amount_allocated=total.incl_tax, currency=total.currency, reference=payment_id) self.add_payment_source(source) # Record Payment event and create the Order(!) self.add_payment_event('pre-auth', total.incl_tax) self._save_order(order_number, submission) # Redirect to Mollie Payment Page url = facade.get_payment_url(payment_id) raise RedirectRequired(url)
def handle_payment(self, order_number, total, **kwargs): try: #confirm the payment action? confirm_txn = confirm_transaction(kwargs['payer_id'], kwargs['token'], kwargs['txn'].amount, kwargs['txn'].currency) except BraintreeError: raise UnableToTakePayment() if not confirm_txn.is_successful: raise UnableToTakePayment() # Record payment source and event source_type, is_created = SourceType.objects.get_or_create( name='PayPal') source = Source(source_type=source_type, currency=confirm_txn.currency, amount_allocated=confirm_txn.amount, amount_debited=confirm_txn.amount) self.add_payment_source(source) self.add_payment_event('Settled', confirm_txn.amount, reference=confirm_txn.correlation_id)
def test_calculates_initial_balance_correctly(self): source = Source(amount_allocated=D('100')) self.assertEqual(D('100'), source.balance)
def handle_payment(self): # request.basket doesn't work b/c the basket is frozen basket = self.get_submitted_basket() venue = basket.get_tickets_venue() self.payment_id = self.execute_payment(venue) # TODO: check that the strategy is correct for Tracks (right stock record). # If basket has tracks, it's probably to use custom strategy. strategy = selector.strategy(request=self.request, user=self.request.user) basket.strategy = strategy order_number = self.checkout_session.get_order_number() user = self.request.user shipping_address = self.get_shipping_address(basket) shipping_method = Repository().get_default_shipping_method( basket=basket, user=user, request=self.request) shipping_charge = shipping_method.calculate(basket) billing_address = self.get_billing_address(shipping_address) order_total = self.get_order_totals(basket, shipping_charge=shipping_charge) total_incl_tax = basket.total_incl_tax # Record payment source order_kwargs = {} venue = basket.get_tickets_venue() if venue: self.tickets = venue.name.lower() order_kwargs['status'] = 'Completed' payment_source = '{} PayPal'.format(venue.name) payment_event = 'Sold' else: payment_source = 'PayPal' payment_event = 'Purchase' source_type, is_created = SourceType.objects.get_or_create( name=payment_source) source = Source(source_type=source_type, currency='USD', amount_allocated=total_incl_tax, amount_debited=total_incl_tax, reference=self.payment_id) self.add_payment_source(source) self.add_payment_event(payment_event, total_incl_tax, reference=self.payment_id) user = self.request.user first_name, last_name = self.checkout_session.get_reservation_name() if user.is_anonymous(): user = None guest_email = self.checkout_session.get_guest_email() order_kwargs['guest_email'] = guest_email if first_name and last_name: order_kwargs.update({ 'first_name': first_name, 'last_name': last_name }) self.handle_order_placement(order_number, user, basket, shipping_address, shipping_method, shipping_charge, billing_address, order_total, **order_kwargs)
def handle_payment(self, order_number, total, **kwargs): """ Try to process the payment using the Square REST API """ square_settings = SquareSettings.get_settings() squareconnect.configuration.access_token = \ square_settings.access_token source_type, __ = SourceType.objects.get_or_create(name='Square') source = Source(source_type=source_type, amount_allocated=total.incl_tax) nonce = self.get_card_nonce() if not nonce: raise PaymentError('No card nonce provided') #TODO: Currency should be a setting stored in the settings model # Set the total amount to charge, in US Cents amount = { 'amount': int(100*float(total.incl_tax)), 'currency': 'USD' } #TODO: Delayed capture can be stored in settings, instead of static # Start the request for authorization, including shipping address and # user email (if provided) body = { 'idempotency_key': self.get_idempotency_key(order_number), 'card_nonce': nonce, 'amount_money': amount, 'delay_capture': True, } # Add user information for chargeback protection if 'email' in kwargs: body['buyer_email_address'] = kwargs['email'] for addr_type in 'shipping_address', 'billing_address': if addr_type in kwargs: addr_object = kwargs[addr_type] address = dict() address['address_line_1'] = addr_object.line1 address['address_line_2'] = addr_object.line2 address['address_line_3'] = addr_object.line3 address['locality'] = addr_object.line4 address['administrative_district_level_1'] = addr_object.state address['postal_code'] = addr_object.postcode address['country'] = addr_object.country.iso_3166_1_a2 address['first_name'] = addr_object.first_name address['last_name'] = addr_object.last_name body[addr_type] = address try: # Charge api_response = api_instance.charge(square_settings.location_id, body) # Save the response ID if not api_response.transaction: raise ApiException(', '.join(api_response.errors)) except ApiException as e: msg = "Exception when calling TransactionApi->charge: {}".format(e) logger.info(msg) raise PaymentError(msg) from ApiException # Request was successful - record the "payment source". As this # request was a 'pre-auth', we set the 'amount_allocated' - if we had # performed an 'auth' request, then we would set 'amount_debited'. self.add_payment_source(source) #TODO: Change payment event type depending on delay/immediate capture # Also record payment event self.add_payment_event('auth', total.incl_tax, reference=api_response.transaction.id)
def place_order(sender, **kwargs): """ collect basket, user, shipping_method and address, order_number, total and pass them to handle_order_placement, but first add payment events and sources """ request = kwargs.get('request', None) or HttpRequest() basket = sender user = basket.owner if basket.owner else AnonymousUser() guest_email = None strategy = selector.strategy(user=user) session_data = shipping_address = shipping_method = None log.debug("initialising: \n basket = %s \n usr = %s \n strategy = %s", basket, user, strategy) basket.strategy = strategy amount_allocated = kwargs['OutSum'] session_key = kwargs['session_key'] order_num = kwargs['order_num'] if session_key is not None: session = SessionStore(session_key=session_key) if len(session.items()): log.debug("Session %s successfully restored", session) request.session = session request.user = user session_data = CheckoutSessionData(request) if isinstance(user, AnonymousUser): guest_email = session_data.get_guest_email() order_placement = RobokassaOrderPlacement() order_placement.request = request if session_data is not None: order_placement.checkout_session = session_data shipping_address = order_placement.get_shipping_address(basket) shipping_method = order_placement.get_shipping_method( basket, shipping_address) total = order_placement.get_order_totals(basket, shipping_method) else: # session not found, lets try to place order anyway log.error(("Session was not restored, trying default order for " "basket #%s"), basket.id) basket.is_shipping_required = False total = prices.Price(currency=basket.currency, excl_tax=basket.total_excl_tax, incl_tax=basket.total_incl_tax) # now create payment source and events source_type, is_created = SourceType.objects.get_or_create( name=u'Робокасса', code='robokassa') source = Source(source_type=source_type, amount_allocated=amount_allocated, amount_debited=amount_allocated) order_placement.add_payment_source(source) order_placement.add_payment_event('allocated', amount_allocated) order_placement.add_payment_event('debited', amount_allocated) post_payment.send(sender=order_placement, user=user, source=source) # all done lets place an order order_placement.handle_order_placement(order_num, user, basket, shipping_address, shipping_method, total, guest_email=guest_email)