Example #1
0
def test_refund_success(env, factory, monkeypatch):
    event, order = env

    def charge_retr(*args, **kwargs):
        def refund_create(amount):
            r = MockedCharge()
            r.id = 'foo'
            r.status = 'succeeded'
            return r

        c = MockedCharge()
        c.refunds.create = refund_create
        return c

    monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
    order.status = Order.STATUS_PAID
    p = order.payments.create(provider='stripe_cc', amount=order.total, info=json.dumps({
        'id': 'ch_123345345'
    }))
    order.save()
    prov = StripeCC(event)
    refund = order.refunds.create(
        provider='stripe_cc', amount=order.total, payment=p,
    )
    prov.execute_refund(refund)
    refund.refresh_from_db()
    assert refund.state == OrderRefund.REFUND_STATE_DONE
Example #2
0
def test_refund_unavailable(env, factory, monkeypatch):
    event, order = env

    def charge_retr(*args, **kwargs):
        def refund_create(amount):
            raise APIConnectionError(message='Foo')

        c = MockedCharge()
        c.refunds.create = refund_create
        return c

    monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
    order.status = Order.STATUS_PAID
    p = order.payments.create(provider='stripe_cc',
                              amount=order.total,
                              info=json.dumps({'id': 'ch_123345345'}))
    order.save()
    prov = StripeCC(event)
    refund = order.refunds.create(provider='stripe_cc',
                                  amount=order.total,
                                  payment=p)
    with pytest.raises(PaymentException):
        prov.execute_refund(refund)
    refund.refresh_from_db()
    assert refund.state != OrderRefund.REFUND_STATE_DONE
Example #3
0
def test_refund_success(env, factory, monkeypatch):
    event, order = env

    def charge_retr(*args, **kwargs):
        def refund_create(amount):
            r = MockedCharge()
            r.id = 'foo'
            r.status = 'succeeded'
            return r

        c = MockedCharge()
        c.refunds.create = refund_create
        return c

    monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
    order.status = Order.STATUS_PAID
    p = order.payments.create(provider='stripe_cc',
                              amount=order.total,
                              info=json.dumps({'id': 'ch_123345345'}))
    order.save()
    prov = StripeCC(event)
    refund = order.refunds.create(
        provider='stripe_cc',
        amount=order.total,
        payment=p,
    )
    prov.execute_refund(refund)
    refund.refresh_from_db()
    assert refund.state == OrderRefund.REFUND_STATE_DONE
Example #4
0
def test_perform_success_zero_decimal_currency(env, factory, monkeypatch):
    event, order = env
    event.currency = 'JPY'
    event.save()

    def charge_create(**kwargs):
        assert kwargs['amount'] == 13
        assert kwargs['currency'] == 'jpy'
        assert kwargs['source'] == 'tok_189fTT2eZvKYlo2CvJKzEzeu'
        c = MockedCharge()
        c.status = 'succeeded'
        c.paid = True
        return c

    monkeypatch.setattr("stripe.Charge.create", charge_create)
    prov = StripeCC(event)
    req = factory.post('/', {
        'stripe_token': 'tok_189fTT2eZvKYlo2CvJKzEzeu',
        'stripe_last4': '4242',
        'stripe_brand': 'Visa'
    })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_token' in req.session
    payment = order.payments.create(
        provider='stripe_cc', amount=order.total
    )
    prov.execute_payment(req, payment)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PAID
Example #5
0
def test_refund_unavailable(env, factory, monkeypatch):
    event, order = env

    def charge_retr(*args, **kwargs):
        def refund_create(amount):
            raise APIConnectionError(message='Foo')

        c = MockedCharge()
        c.refunds.create = refund_create
        return c

    monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
    order.status = Order.STATUS_PAID
    p = order.payments.create(provider='stripe_cc', amount=order.total, info=json.dumps({
        'id': 'ch_123345345'
    }))
    order.save()
    prov = StripeCC(event)
    refund = order.refunds.create(
        provider='stripe_cc', amount=order.total, payment=p
    )
    with pytest.raises(PaymentException):
        prov.execute_refund(refund)
    refund.refresh_from_db()
    assert refund.state != OrderRefund.REFUND_STATE_DONE
Example #6
0
def test_perform_failed(env, factory, monkeypatch):
    event, order = env

    def charge_create(**kwargs):
        c = MockedCharge()
        c.status = 'failed'
        c.paid = True
        c.failure_message = 'Foo'
        return c

    monkeypatch.setattr("stripe.Charge.create", charge_create)
    prov = StripeCC(event)
    req = factory.post('/', {
        'stripe_token': 'tok_189fTT2eZvKYlo2CvJKzEzeu',
        'stripe_last4': '4242',
        'stripe_brand': 'Visa'
    })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_token' in req.session
    with pytest.raises(PaymentException):
        payment = order.payments.create(
            provider='stripe_cc', amount=order.total
        )
        prov.execute_payment(req, payment)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PENDING
Example #7
0
def stripe_verify_domain(event, domain):
    from pretix.plugins.stripe.payment import StripeCC
    prov = StripeCC(event)
    account = get_stripe_account_key(prov)

    # Yes, we could just use the **prov.api_kwargs
    # But since we absolutely need to always issue this call with live keys,
    # we're building our api_kwargs here by hand.
    # Only if no live connect secret key is set, we'll fall back to the testmode keys.
    # But this should never happen except in scenarios where pretix runs in devmode.
    if prov.settings.connect_client_id and prov.settings.connect_user_id:
        api_kwargs = {
            'api_key': prov.settings.connect_secret_key
            or prov.settings.connect_test_secret_key,
            'stripe_account': prov.settings.connect_user_id
        }
    else:
        api_kwargs = {
            'api_key': prov.settings.secret_key,
        }

    if RegisteredApplePayDomain.objects.filter(account=account,
                                               domain=domain).exists():
        return

    try:
        resp = stripe.ApplePayDomain.create(domain_name=domain, **api_kwargs)
    except stripe.error.StripeError:
        logger.exception('Could not verify domain with Stripe')
    else:
        if resp.livemode:
            RegisteredApplePayDomain.objects.create(domain=domain,
                                                    account=account)
Example #8
0
def paymentintent_webhook(event, event_json, paymentintent_id, rso):
    prov = StripeCC(event)
    prov._init_api()

    try:
        paymentintent = stripe.PaymentIntent.retrieve(paymentintent_id, **prov.api_kwargs)
    except stripe.error.StripeError:
        logger.exception('Stripe error on webhook. Event data: %s' % str(event_json))
        return HttpResponse('Charge not found', status=500)

    for charge in paymentintent.charges.data:
        ReferencedStripeObject.objects.get_or_create(
            reference=charge.id,
            defaults={'order': rso.payment.order, 'payment': rso.payment}
        )

    return HttpResponse(status=200)
Example #9
0
def test_perform_failed(env, factory, monkeypatch):
    event, order = env

    def charge_create(**kwargs):
        c = MockedCharge()
        c.status = 'failed'
        c.paid = True
        c.failure_message = 'Foo'
        return c

    monkeypatch.setattr("stripe.Charge.create", charge_create)
    prov = StripeCC(event)
    req = factory.post(
        '/', {
            'stripe_token': 'tok_189fTT2eZvKYlo2CvJKzEzeu',
            'stripe_last4': '4242',
            'stripe_brand': 'Visa'
        })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_token' in req.session
    with pytest.raises(PaymentException):
        prov.payment_perform(req, order)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PENDING
Example #10
0
def test_perform_success(env, factory, monkeypatch):
    event, order = env

    def charge_create(**kwargs):
        assert kwargs['amount'] == 1337
        assert kwargs['currency'] == 'eur'
        assert kwargs['source'] == 'tok_189fTT2eZvKYlo2CvJKzEzeu'
        c = MockedCharge()
        c.status = 'succeeded'
        c.paid = True
        return c

    monkeypatch.setattr("stripe.Charge.create", charge_create)
    prov = StripeCC(event)
    req = factory.post('/', {
        'stripe_token': 'tok_189fTT2eZvKYlo2CvJKzEzeu',
        'stripe_last4': '4242',
        'stripe_brand': 'Visa'
    })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_token' in req.session
    payment = order.payments.create(
        provider='stripe_cc', amount=order.total
    )
    prov.execute_payment(req, payment)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PAID
Example #11
0
def test_perform_success_zero_decimal_currency(env, factory, monkeypatch):
    event, order = env
    event.currency = 'JPY'
    event.save()

    def paymentintent_create(**kwargs):
        assert kwargs['amount'] == 13
        assert kwargs['currency'] == 'jpy'
        assert kwargs['payment_method'] == 'pm_189fTT2eZvKYlo2CvJKzEzeu'
        c = MockedPaymentintent()
        c.status = 'succeeded'
        c.charges.data[0].paid = True
        return c

    monkeypatch.setattr("stripe.PaymentIntent.create", paymentintent_create)
    prov = StripeCC(event)
    req = factory.post(
        '/', {
            'stripe_payment_method_id': 'pm_189fTT2eZvKYlo2CvJKzEzeu',
            'stripe_last4': '4242',
            'stripe_brand': 'Visa'
        })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_payment_method_id' in req.session
    payment = order.payments.create(provider='stripe_cc', amount=order.total)
    prov.execute_payment(req, payment)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PAID
Example #12
0
def test_perform_success_zero_decimal_currency(env, factory, monkeypatch):
    event, order = env
    event.currency = 'JPY'
    event.save()

    def charge_create(**kwargs):
        assert kwargs['amount'] == 13
        assert kwargs['currency'] == 'jpy'
        assert kwargs['source'] == 'tok_189fTT2eZvKYlo2CvJKzEzeu'
        c = MockedCharge()
        c.status = 'succeeded'
        c.paid = True
        return c

    monkeypatch.setattr("stripe.Charge.create", charge_create)
    prov = StripeCC(event)
    req = factory.post(
        '/', {
            'stripe_token': 'tok_189fTT2eZvKYlo2CvJKzEzeu',
            'stripe_last4': '4242',
            'stripe_brand': 'Visa'
        })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_token' in req.session
    prov.payment_perform(req, order)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PAID
Example #13
0
def test_perform_failed(env, factory, monkeypatch):
    event, order = env

    def paymentintent_create(**kwargs):
        assert kwargs['amount'] == 1337
        assert kwargs['currency'] == 'eur'
        assert kwargs['payment_method'] == 'pm_189fTT2eZvKYlo2CvJKzEzeu'
        c = MockedPaymentintent()
        c.status = 'failed'
        c.failure_message = 'Foo'
        c.charges.data[0].paid = True
        c.last_payment_error = Object()
        c.last_payment_error.message = "Foo"
        return c

    monkeypatch.setattr("stripe.PaymentIntent.create", paymentintent_create)
    prov = StripeCC(event)
    req = factory.post(
        '/', {
            'stripe_payment_method_id': 'pm_189fTT2eZvKYlo2CvJKzEzeu',
            'stripe_last4': '4242',
            'stripe_brand': 'Visa'
        })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_payment_method_id' in req.session
    with pytest.raises(PaymentException):
        payment = order.payments.create(provider='stripe_cc',
                                        amount=order.total)
        prov.execute_payment(req, payment)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PENDING
Example #14
0
def charge_webhook(event, event_json, charge_id):
    prov = StripeCC(event)
    prov._init_api()
    try:
        charge = stripe.Charge.retrieve(charge_id)
    except stripe.error.StripeError:
        logger.exception('Stripe error on webhook. Event data: %s' %
                         str(event_json))
        return HttpResponse('Charge not found', status=500)

    metadata = charge['metadata']
    if 'event' not in metadata:
        return HttpResponse('Event not given in charge metadata', status=200)

    if int(metadata['event']) != event.pk:
        return HttpResponse('Not interested in this event', status=200)

    try:
        order = event.orders.get(id=metadata['order'],
                                 payment_provider__startswith='stripe')
    except Order.DoesNotExist:
        return HttpResponse('Order not found', status=200)

    if order.payment_provider != prov.identifier:
        prov = event.get_payment_providers()[order.payment_provider]
        prov._init_api()

    order.log_action('pretix.plugins.stripe.event', data=event_json)

    is_refund = charge['refunds']['total_count'] or charge['dispute']
    if order.status == Order.STATUS_PAID and is_refund:
        RequiredAction.objects.create(
            event=event,
            action_type='pretix.plugins.stripe.refund',
            data=json.dumps({
                'order': order.code,
                'charge': charge_id
            }))
    elif order.status in (
            Order.STATUS_PENDING, Order.STATUS_EXPIRED
    ) and charge['status'] == 'succeeded' and not is_refund:
        try:
            mark_order_paid(order, user=None)
        except LockTimeoutException:
            return HttpResponse("Lock timeout, please try again.", status=503)
        except Quota.QuotaExceededException:
            if not RequiredAction.objects.filter(
                    event=event,
                    action_type='pretix.plugins.stripe.overpaid',
                    data__icontains=order.code).exists():
                RequiredAction.objects.create(
                    event=event,
                    action_type='pretix.plugins.stripe.overpaid',
                    data=json.dumps({
                        'order': order.code,
                        'charge': charge.id
                    }))

    return HttpResponse(status=200)
Example #15
0
def test_refund_success(env, factory, monkeypatch):
    event, order = env

    def charge_retr(*args, **kwargs):
        def refund_create():
            pass

        c = MockedCharge()
        c.refunds = MockedCharge()
        c.refunds.create = refund_create
        return c

    monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
    order.status = Order.STATUS_PAID
    order.payment_info = json.dumps({'id': 'ch_123345345'})
    order.save()
    prov = StripeCC(event)
    req = factory.post('/', data={'auto_refund': 'auto'})
    req.user = None
    prov.order_control_refund_perform(req, order)
    order.refresh_from_db()
    assert order.status == Order.STATUS_REFUNDED
Example #16
0
def test_refund_unavailable(env, factory, monkeypatch):
    event, order = env

    def charge_retr(*args, **kwargs):
        def refund_create():
            raise APIConnectionError(message='Foo')

        c = MockedCharge()
        c.refunds = object()
        c.refunds.create = refund_create()
        return c

    monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
    order.status = Order.STATUS_PAID
    order.payment_info = json.dumps({'id': 'ch_123345345'})
    order.save()
    prov = StripeCC(event)
    req = factory.get('/')
    req.user = None
    prov.order_control_refund_perform(req, order)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PAID
Example #17
0
def stripe_verify_domain(event, domain):
    from pretix.plugins.stripe.payment import StripeCC
    prov = StripeCC(event)
    account = get_stripe_account_key(prov)

    if RegisteredApplePayDomain.objects.filter(account=account,
                                               domain=domain).exists():
        return

    try:
        resp = stripe.ApplePayDomain.create(domain_name=domain,
                                            **prov.api_kwargs)
    except stripe.error.StripeError:
        logger.exception('Could not verify domain with Stripe')
    else:
        if resp.livemode:
            RegisteredApplePayDomain.objects.create(domain=domain,
                                                    account=account)
Example #18
0
def source_webhook(event, event_json, source_id):
    prov = StripeCC(event)
    prov._init_api()
    try:
        src = stripe.Source.retrieve(source_id, **prov.api_kwargs)
    except stripe.error.StripeError:
        logger.exception('Stripe error on webhook. Event data: %s' %
                         str(event_json))
        return HttpResponse('Charge not found', status=500)

    metadata = src['metadata']
    if 'event' not in metadata:
        return HttpResponse('Event not given in charge metadata', status=200)

    if int(metadata['event']) != event.pk:
        return HttpResponse('Not interested in this event', status=200)

    with transaction.atomic():
        try:
            order = event.orders.get(id=metadata['order'],
                                     payment_provider__startswith='stripe')
        except Order.DoesNotExist:
            return HttpResponse('Order not found', status=200)

        if order.payment_provider != prov.identifier:
            prov = event.get_payment_providers()[order.payment_provider]
            prov._init_api()

        order.log_action('pretix.plugins.stripe.event', data=event_json)
        go = (event_json['type'] == 'source.chargeable'
              and order.status == Order.STATUS_PENDING
              and src.status == 'chargeable')
        if go:
            try:
                prov._charge_source(None, source_id, order)
            except PaymentException:
                logger.exception('Webhook error')

    return HttpResponse(status=200)
Example #19
0
def test_perform_card_error(env, factory, monkeypatch):
    event, order = env

    def charge_create(**kwargs):
        raise CardError(message='Foo', param='foo', code=100)

    monkeypatch.setattr("stripe.Charge.create", charge_create)
    prov = StripeCC(event)
    req = factory.post(
        '/', {
            'stripe_token': 'tok_189fTT2eZvKYlo2CvJKzEzeu',
            'stripe_last4': '4242',
            'stripe_brand': 'Visa'
        })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_token' in req.session
    with pytest.raises(PaymentException):
        prov.payment_perform(req, order)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PENDING
Example #20
0
def test_perform_stripe_error(env, factory, monkeypatch):
    event, order = env

    def paymentintent_create(**kwargs):
        raise CardError(message='Foo', param='foo', code=100)

    monkeypatch.setattr("stripe.PaymentIntent.create", paymentintent_create)
    prov = StripeCC(event)
    req = factory.post(
        '/', {
            'stripe_payment_method_id': 'pm_189fTT2eZvKYlo2CvJKzEzeu',
            'stripe_last4': '4242',
            'stripe_brand': 'Visa'
        })
    req.session = {}
    prov.checkout_prepare(req, {})
    assert 'payment_stripe_payment_method_id' in req.session
    with pytest.raises(PaymentException):
        payment = order.payments.create(provider='stripe_cc',
                                        amount=order.total)
        prov.execute_payment(req, payment)
    order.refresh_from_db()
    assert order.status == Order.STATUS_PENDING
Example #21
0
def source_webhook(event, event_json, source_id, rso):
    prov = StripeCC(event)
    prov._init_api()
    try:
        src = stripe.Source.retrieve(source_id, **prov.api_kwargs)
    except stripe.error.StripeError:
        logger.exception('Stripe error on webhook. Event data: %s' % str(event_json))
        return HttpResponse('Charge not found', status=500)

    metadata = src['metadata']
    if 'event' not in metadata:
        return HttpResponse('Event not given in charge metadata', status=200)

    if int(metadata['event']) != event.pk:
        return HttpResponse('Not interested in this event', status=200)

    with transaction.atomic():
        if rso and rso.payment:
            order = rso.payment.order
            payment = rso.payment
        elif rso:
            order = rso.order
            payment = None
        else:
            try:
                order = event.orders.get(id=metadata['order'])
            except Order.DoesNotExist:
                return HttpResponse('Order not found', status=200)
            payment = None

        if not payment:
            payment = order.payments.filter(
                info__icontains=src['id'],
                provider__startswith='stripe',
                amount=prov._amount_to_decimal(src['amount']) if src['amount'] is not None else order.total,
            ).last()
        if not payment:
            payment = order.payments.create(
                state=OrderPayment.PAYMENT_STATE_CREATED,
                provider=SOURCE_TYPES.get(src['type'], 'stripe'),
                amount=prov._amount_to_decimal(src['amount']) if src['amount'] is not None else order.total,
                info=str(src),
            )

        if payment.provider != prov.identifier:
            prov = payment.payment_provider
            prov._init_api()

        order.log_action('pretix.plugins.stripe.event', data=event_json)
        go = (event_json['type'] == 'source.chargeable' and
              payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED) and
              src.status == 'chargeable')
        if go:
            try:
                prov._charge_source(None, source_id, payment)
            except PaymentException:
                logger.exception('Webhook error')

        elif src.status == 'failed':
            payment.info = str(src)
            payment.state = OrderPayment.PAYMENT_STATE_FAILED
            payment.order.log_action('pretix.event.order.payment.failed', {
                'local_id': payment.local_id,
                'provider': payment.provider,
                'info': str(src)
            })
            payment.save()

    return HttpResponse(status=200)
Example #22
0
def charge_webhook(event, event_json, charge_id, rso):
    prov = StripeCC(event)
    prov._init_api()
    try:
        charge = stripe.Charge.retrieve(charge_id, expand=['dispute'], **prov.api_kwargs)
    except stripe.error.StripeError:
        logger.exception('Stripe error on webhook. Event data: %s' % str(event_json))
        return HttpResponse('Charge not found', status=500)

    metadata = charge['metadata']
    if 'event' not in metadata:
        return HttpResponse('Event not given in charge metadata', status=200)

    if int(metadata['event']) != event.pk:
        return HttpResponse('Not interested in this event', status=200)

    if rso and rso.payment:
        order = rso.payment.order
        payment = rso.payment
    elif rso:
        order = rso.order
        payment = None
    else:
        try:
            order = event.orders.get(id=metadata['order'])
        except Order.DoesNotExist:
            return HttpResponse('Order not found', status=200)
        payment = None

    if not payment:
        payment = order.payments.filter(
            info__icontains=charge['id'],
            provider__startswith='stripe',
            amount=prov._amount_to_decimal(charge['amount']),
        ).last()
    if not payment:
        payment = order.payments.create(
            state=OrderPayment.PAYMENT_STATE_CREATED,
            provider=SOURCE_TYPES.get(charge['source'].get('type', charge['source'].get('object', 'card')), 'stripe'),
            amount=prov._amount_to_decimal(charge['amount']),
            info=str(charge),
        )

    if payment.provider != prov.identifier:
        prov = payment.payment_provider
        prov._init_api()

    order.log_action('pretix.plugins.stripe.event', data=event_json)

    is_refund = charge['refunds']['total_count'] or charge['dispute']
    if is_refund:
        known_refunds = [r.info_data.get('id') for r in payment.refunds.all()]
        migrated_refund_amounts = [r.amount for r in payment.refunds.all() if not r.info_data.get('id')]
        for r in charge['refunds']['data']:
            a = prov._amount_to_decimal(r['amount'])
            if r['status'] in ('failed', 'canceled'):
                continue

            if a in migrated_refund_amounts:
                migrated_refund_amounts.remove(a)
                continue

            if r['id'] not in known_refunds:
                payment.create_external_refund(
                    amount=a,
                    info=str(r)
                )
        if charge['dispute']:
            if charge['dispute']['status'] != 'won' and charge['dispute']['id'] not in known_refunds:
                a = prov._amount_to_decimal(charge['dispute']['amount'])
                if a in migrated_refund_amounts:
                    migrated_refund_amounts.remove(a)
                else:
                    payment.create_external_refund(
                        amount=a,
                        info=str(charge['dispute'])
                    )
    elif charge['status'] == 'succeeded' and payment.state in (OrderPayment.PAYMENT_STATE_PENDING,
                                                               OrderPayment.PAYMENT_STATE_CREATED,
                                                               OrderPayment.PAYMENT_STATE_CANCELED,
                                                               OrderPayment.PAYMENT_STATE_FAILED):
        try:
            payment.confirm()
        except LockTimeoutException:
            return HttpResponse("Lock timeout, please try again.", status=503)
        except Quota.QuotaExceededException:
            pass
    elif charge['status'] == 'failed' and payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED):
        payment.info = str(charge)
        payment.state = OrderPayment.PAYMENT_STATE_FAILED
        payment.save()
        payment.order.log_action('pretix.event.order.payment.failed', {
            'local_id': payment.local_id,
            'provider': payment.provider,
            'info': str(charge)
        })

    return HttpResponse(status=200)
Example #23
0
def charge_webhook(event, event_json, charge_id, rso):
    prov = StripeCC(event)
    prov._init_api()
    try:
        charge = stripe.Charge.retrieve(charge_id, expand=['dispute'], **prov.api_kwargs)
    except stripe.error.StripeError:
        logger.exception('Stripe error on webhook. Event data: %s' % str(event_json))
        return HttpResponse('Charge not found', status=500)

    metadata = charge['metadata']
    if 'event' not in metadata:
        return HttpResponse('Event not given in charge metadata', status=200)

    if int(metadata['event']) != event.pk:
        return HttpResponse('Not interested in this event', status=200)

    if rso and rso.payment:
        order = rso.payment.order
        payment = rso.payment
    elif rso:
        order = rso.order
        payment = None
    else:
        try:
            order = event.orders.get(id=metadata['order'])
        except Order.DoesNotExist:
            return HttpResponse('Order not found', status=200)
        payment = None

    if not payment:
        payment = order.payments.filter(
            info__icontains=charge['id'],
            provider__startswith='stripe',
            amount=prov._amount_to_decimal(charge['amount']),
        ).last()
    if not payment:
        payment = order.payments.create(
            state=OrderPayment.PAYMENT_STATE_CREATED,
            provider=SOURCE_TYPES.get(charge['source'].get('type', charge['source'].get('object', 'card')), 'stripe'),
            amount=prov._amount_to_decimal(charge['amount']),
            info=str(charge),
        )

    if payment.provider != prov.identifier:
        prov = payment.payment_provider
        prov._init_api()

    order.log_action('pretix.plugins.stripe.event', data=event_json)

    is_refund = charge['refunds']['total_count'] or charge['dispute']
    if is_refund:
        known_refunds = [r.info_data.get('id') for r in payment.refunds.all()]
        migrated_refund_amounts = [r.amount for r in payment.refunds.all() if not r.info_data.get('id')]
        for r in charge['refunds']['data']:
            a = prov._amount_to_decimal(r['amount'])
            if r['status'] in ('failed', 'canceled'):
                continue

            if a in migrated_refund_amounts:
                migrated_refund_amounts.remove(a)
                continue

            if r['id'] not in known_refunds:
                payment.create_external_refund(
                    amount=a,
                    info=str(r)
                )
        if charge['dispute']:
            if charge['dispute']['status'] != 'won' and charge['dispute']['id'] not in known_refunds:
                a = prov._amount_to_decimal(charge['dispute']['amount'])
                if a in migrated_refund_amounts:
                    migrated_refund_amounts.remove(a)
                else:
                    payment.create_external_refund(
                        amount=a,
                        info=str(charge['dispute'])
                    )
    elif charge['status'] == 'succeeded' and payment.state in (OrderPayment.PAYMENT_STATE_PENDING,
                                                               OrderPayment.PAYMENT_STATE_CREATED,
                                                               OrderPayment.PAYMENT_STATE_CANCELED,
                                                               OrderPayment.PAYMENT_STATE_FAILED):
        try:
            payment.confirm()
        except LockTimeoutException:
            return HttpResponse("Lock timeout, please try again.", status=503)
        except Quota.QuotaExceededException:
            pass
    elif charge['status'] == 'failed' and payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED):
            payment.info = str(charge)
            payment.state = OrderPayment.PAYMENT_STATE_FAILED
            payment.save()
            payment.order.log_action('pretix.event.order.payment.failed', {
                'local_id': payment.local_id,
                'provider': payment.provider,
                'info': str(charge)
            })

    return HttpResponse(status=200)
Example #24
0
def source_webhook(event, event_json, source_id, rso):
    prov = StripeCC(event)
    prov._init_api()
    try:
        src = stripe.Source.retrieve(source_id, **prov.api_kwargs)
    except stripe.error.StripeError:
        logger.exception('Stripe error on webhook. Event data: %s' % str(event_json))
        return HttpResponse('Charge not found', status=500)

    metadata = src['metadata']
    if 'event' not in metadata:
        return HttpResponse('Event not given in charge metadata', status=200)

    if int(metadata['event']) != event.pk:
        return HttpResponse('Not interested in this event', status=200)

    with transaction.atomic():
        if rso and rso.payment:
            order = rso.payment.order
            payment = rso.payment
        elif rso:
            order = rso.order
            payment = None
        else:
            try:
                order = event.orders.get(id=metadata['order'])
            except Order.DoesNotExist:
                return HttpResponse('Order not found', status=200)
            payment = None

        if not payment:
            payment = order.payments.filter(
                info__icontains=src['id'],
                provider__startswith='stripe',
                amount=prov._amount_to_decimal(src['amount']) if src['amount'] is not None else order.total,
            ).last()
        if not payment:
            payment = order.payments.create(
                state=OrderPayment.PAYMENT_STATE_CREATED,
                provider=SOURCE_TYPES.get(src['type'], 'stripe'),
                amount=prov._amount_to_decimal(src['amount']) if src['amount'] is not None else order.total,
                info=str(src),
            )

        if payment.provider != prov.identifier:
            prov = payment.payment_provider
            prov._init_api()

        order.log_action('pretix.plugins.stripe.event', data=event_json)
        go = (event_json['type'] == 'source.chargeable' and
              payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED) and
              src.status == 'chargeable')
        if go:
            try:
                prov._charge_source(None, source_id, payment)
            except PaymentException:
                logger.exception('Webhook error')

        elif src.status == 'failed':
            payment.info = str(src)
            payment.state = OrderPayment.PAYMENT_STATE_FAILED
            payment.order.log_action('pretix.event.order.payment.failed', {
                'local_id': payment.local_id,
                'provider': payment.provider,
                'info': str(src)
            })
            payment.save()
        elif src.status == 'canceled' and payment.state in (Order.STATUS_PENDING, OrderPayment.PAYMENT_STATE_CREATED):
            payment.info = str(src)
            payment.state = OrderPayment.PAYMENT_STATE_CANCELED
            payment.save()

    return HttpResponse(status=200)