Exemplo n.º 1
0
 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)
Exemplo n.º 2
0
 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)
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
    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)
Exemplo n.º 7
0
    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)
Exemplo n.º 8
0
 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)
Exemplo n.º 9
0
    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)
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
    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)
Exemplo n.º 14
0
    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)
Exemplo n.º 15
0
    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)
Exemplo n.º 16
0
    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)
Exemplo n.º 17
0
    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)
Exemplo n.º 18
0
    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)
Exemplo n.º 19
0
    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)
Exemplo n.º 20
0
 def test_calculates_initial_balance_correctly(self):
     source = Source(amount_allocated=D('100'))
     self.assertEqual(D('100'), source.balance)
Exemplo n.º 21
0
    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)
Exemplo n.º 22
0
    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)
Exemplo n.º 23
0
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)