Ejemplo n.º 1
0
    def create_fake_tickets(self, user):
        if random.random() < 0.3:
            return

        if random.random() < 0.2:
            currency = "EUR"
        else:
            currency = "GBP"
        b = Basket(user, currency)

        pt = PriceTier.query.filter_by(name="full-std").one()
        b[pt] = int(round(random.uniform(1, 4)))

        if random.random() < 0.5:
            pt = PriceTier.query.filter_by(name="parking").one()
            b[pt] = 1

        b.create_purchases()
        b.ensure_purchase_capacity()

        payment_type = {
            "gc": GoCardlessPayment,
            "bank": BankPayment,
            "stripe": StripePayment,
        }.get(random_state({"gc": 0.3, "bank": 0.2, "stripe": 0.5}))

        payment = b.create_payment(payment_type)

        if random.random() < 0.8:
            payment.paid()
        else:
            payment.cancel()
Ejemplo n.º 2
0
    def create_fixtures(self):
        self.user1_email = self.user1_email_template.format(random_string(8))
        self.user2_email = self.user2_email_template.format(random_string(8))
        self.user1 = User(self.user1_email, 'test_user1')
        self.user2 = User(self.user2_email, 'test_user2')
        self.db.session.add(self.user1)
        self.db.session.add(self.user2)

        self.group_name = self.group_template.format(random_string(8))
        self.group = ProductGroup(type='admissions', name=self.group_name)
        self.product = Product(name=self.product_name, parent=self.group)
        self.tier = PriceTier(name=self.tier_name, parent=self.product)
        self.price = Price(price_tier=self.tier, currency='GBP', price_int=666)

        self.db.session.add(self.price)
        self.db.session.commit()

        # PriceTier needs to have been committed before this
        basket = Basket(self.user1, 'GBP')
        basket[self.tier] = 1
        basket.create_purchases()
        basket.ensure_purchase_capacity()
        payment = basket.create_payment(BankPayment)
        assert len(payment.purchases) == 1
        self.db.session.commit()
Ejemplo n.º 3
0
    def create_purchases(self, tier, count):
        basket = Basket(self.user, 'GBP')
        basket[tier] = count
        basket.create_purchases()
        basket.ensure_purchase_capacity()
        payment = basket.create_payment(BankPayment)
        assert len(payment.purchases) == count
        self.db.session.commit()

        return payment.purchases
Ejemplo n.º 4
0
def create_purchases(tier, count, user):
    basket = Basket(user, "GBP")
    basket[tier] = count
    basket.create_purchases()
    basket.ensure_purchase_capacity()
    payment = basket.create_payment(BankPayment)

    db.session.add(payment)
    db.session.commit()
    assert len(payment.purchases) == count
    return payment.purchases
Ejemplo n.º 5
0
def create_purchases(tier, count, user):
    basket = Basket(user, 'GBP')
    basket[tier] = count
    basket.create_purchases()
    basket.ensure_purchase_capacity()
    payment = basket.create_payment(BankPayment)

    db.session.add(payment)
    db.session.commit()
    assert len(payment.purchases) == count
    return payment.purchases
Ejemplo n.º 6
0
    def create_fake_tickets(self, user):
        if random.random() < 0.3:
            return

        if random.random() < 0.2:
            currency = 'EUR'
        else:
            currency = 'GBP'
        b = Basket(user, currency)

        pt = PriceTier.query.filter_by(name='full-std').one()
        b[pt] = int(round(random.uniform(1, 4)))

        if random.random() < 0.5:
            pt = PriceTier.query.filter_by(name='parking').one()
            b[pt] = 1

        b.create_purchases()
        b.ensure_purchase_capacity()

        payment_type = {
            'gc': GoCardlessPayment,
            'bank': BankPayment,
            'stripe': StripePayment
        }.get(random_state({
            'gc': 0.3,
            'bank': 0.2,
            'stripe': 0.5
        }))

        payment = b.create_payment(payment_type)

        if random.random() < 0.8:
            payment.paid()
        else:
            payment.cancel()
Ejemplo n.º 7
0
def start_payment(form: TicketPaymentForm, basket: Basket, flow: str):
    if Decimal(form.basket_total.data) != Decimal(basket.total):
        # Check that the user's basket approximately matches what we told them they were paying.
        price_changed.inc()
        app.logger.warn(
            "User's basket has changed value %s -> %s",
            form.basket_total.data,
            basket.total,
        )
        flash(
            """The items you selected have changed, possibly because you had two windows open.
              Please verify that you've selected the correct items."""
        )
        return redirect(url_for("tickets.pay", flow=flow))

    user = current_user

    if user.is_anonymous:
        try:
            new_user = create_current_user(form.email.data, form.name.data)
        except IntegrityError as e:
            app.logger.warn("Adding user raised %r, possible double-click", e)
            return redirect(url_for("tickets.pay", flow=flow))

        user = new_user
    elif user.name == user.email:
        user.name = form.name.data

    if form.allow_promo.data:
        user.promo_opt_in = True

    if basket.requires_shipping:
        if not user.shipping:
            user.shipping = UserShipping()

        user.shipping.address_1 = form.address_1.data
        user.shipping.address_2 = form.address_2.data
        user.shipping.town = form.town.data
        user.shipping.postcode = form.postcode.data
        user.shipping.country = form.country.data

    payment_type = form.get_payment_class()

    basket.user = user
    try:
        payment = basket.create_payment(payment_type)
    except VoucherUsedError as e:
        # Voucher has been used since we last checked it at the "choose" stage.
        app.logger.exception("Voucher used at payment stage")
        flash(
            "The voucher you've used does not allow you to buy this many adult tickets. "
            "Please choose fewer tickets."
        )
        db.session.rollback()
        return redirect(url_for("tickets.main", flow=flow))

    basket.cancel_surplus_purchases()
    db.session.commit()

    # Remove voucher ID from session, if it exists.
    try:
        del session["ticket_voucher"]
    except KeyError:
        pass

    Basket.clear_from_session()

    if not payment:
        empty_baskets.inc()
        app.logger.warn("User tried to pay for empty basket")
        flash(
            "We're sorry, your session information has been lost. Please try ordering again."
        )
        return redirect(url_for("tickets.main", flow=flow))

    if payment_type == BankPayment:
        return transfer_start(payment)
    if payment_type == StripePayment:
        return stripe_start(payment)

    app.logger.exception(f"Unexpected payment_type: {repr(payment_type)}")
    flash("We're sorry, an unexpected error occurred. Please try ordering again.")
    return redirect(url_for("tickets.main", flow=flow))
Ejemplo n.º 8
0
def test_create_stripe_purchase(user, app, monkeypatch):
    # Add some tickets to a basket (/tickets/choose)
    basket = Basket(user, "GBP")
    tier = PriceTier.query.filter_by(name="full-std").one_or_none()
    basket[tier] = 2

    basket.create_purchases()
    basket.ensure_purchase_capacity()
    db.session.commit()

    # This matches the intent ID in stored fixtures
    intent_id = "pi_1GUslpIcI91cWsdeheAuRsyg"

    with app.test_request_context("/tickets/pay"):
        login_user(user)
        payment = basket.create_payment(StripePayment)
        stripe_start(payment)

    assert payment.state == "new"

    with app.test_request_context(f"/pay/stripe/{payment.id}/capture"):
        login_user(user)
        # Start capture process - this creates a payment intent from fake-stripe
        stripe_capture(payment.id)

        # A payment_intent.created webhook should be generated here, but it
        # doesn't cause any action on our end so we don't simulate this.
        assert payment.intent_id == intent_id
        assert payment.state == "new"

        # User is now on the Stripe form, which captures the card details.
        # Once this is complete, payment details are sent to Stripe and the form
        # submission triggers stripe_capture_post
        stripe_capture_post(payment.id)

    assert payment.state == "charging"

    with app.test_request_context("/stripe-webhook"):
        # Stripe will now send a webhook to notify us of the payment success.
        stripe_payment_intent_updated(
            "payment_intent.succeeded",
            load_webhook_fixture("payment_intent.succeeded"))
        # A charge.succeeded webhook is also sent but we ignore it.

    assert payment.state == "paid"
    assert all(
        purchase.state == "paid" for purchase in
        payment.purchases), "Purchases should be marked as paid after payment"

    # Payment is all paid. Now we test refunding it.
    # Create a refund request for the entire payment, with £20 donation.
    refund_request = RefundRequest(payment=payment,
                                   donation=20,
                                   currency=payment.currency)
    payment.state = "refund-requested"
    db.session.add(refund_request)
    db.session.commit()

    handle_refund_request(refund_request)

    with app.test_request_context("/stripe-webhook"):
        # charge.refunded webhook. We do process this but currently we don't use it for anything.
        stripe_charge_refunded("charge.refunded",
                               load_webhook_fixture("charge.refunded"))

    # Payment should be marked as fully refunded.
    assert payment.state == "refunded"
    assert all(purchase.state == "refunded" for purchase in payment.purchases
               ), "Purchases should be marked as refunded after refund"