def retry(request, order): try: order = Order.objects.current.get( user=request.user, code=order, ) except Order.DoesNotExist: return # TODO: Handle this provider = Paypal(order.event) provider.init_api() if 'token' in request.GET: if 'PayerID' in request.GET: payment = paypalrestsdk.Payment.find(request.session.get('payment_paypal_id')) provider._execute_payment(payment, request, order) else: messages.error(request, _('It looks like you cancelled the PayPal payment')) else: payment = paypalrestsdk.Payment({ 'intent': 'sale', 'payer': { "payment_method": "paypal", }, "redirect_urls": { "return_url": build_absolute_uri('plugins:paypal:retry', kwargs={ 'order': order.code }), "cancel_url": build_absolute_uri('plugins:paypal:retry', kwargs={ 'order': order.code }), }, "transactions": [ { "item_list": { "items": [ { "name": 'Order %s' % order.code, "quantity": 1, "price": str(order.total), "currency": order.event.currency } ] }, "amount": { "currency": order.event.currency, "total": str(order.total) }, "description": __('Event tickets for %s') % order.event.name } ] }) resp = provider._create_payment(request, payment) if resp: return redirect(resp) return redirect(eventreverse(order.event, 'presale:event.order', kwargs={ 'order': order.code, 'secret': order.secret }) + '?paid=yes')
def oauth_return(request, *args, **kwargs): if 'payment_paypal_oauth_event' not in request.session: messages.error(request, _('An error occurred during connecting with PayPal, please try again.')) return redirect(reverse('control:index')) event = get_object_or_404(Event, pk=request.session['payment_paypal_oauth_event']) prov = Paypal(event) prov.init_api() try: tokeninfo = Tokeninfo.create(request.GET.get('code')) userinfo = Tokeninfo.create_with_refresh_token(tokeninfo['refresh_token']).userinfo() except: logger.exception('Failed to obtain OAuth token') messages.error(request, _('An error occurred during connecting with PayPal, please try again.')) else: messages.success(request, _('Your PayPal account is now connected to pretix. You can change the settings in ' 'detail below.')) event.settings.payment_paypal_connect_refresh_token = tokeninfo['refresh_token'] event.settings.payment_paypal_connect_user_id = userinfo.email return redirect(reverse('control:event.settings.payment.provider', kwargs={ 'organizer': event.organizer.slug, 'event': event.slug, 'provider': 'paypal' }))
def webhook(request, *args, **kwargs): event_body = request.body.decode('utf-8').strip() event_json = json.loads(event_body) prov = Paypal(request.event) prov.init_api() # We do not check the signature, we just use it as a trigger to look the charge up. if event_json['resource_type'] not in ('sale', 'refund'): return HttpResponse("Not interested in this resource type", status=200) try: if event_json['resource_type'] == 'sale': sale = paypalrestsdk.Sale.find(event_json['resource']['id']) else: sale = paypalrestsdk.Sale.find(event_json['resource']['sale_id']) except: logger.exception('PayPal error on webhook. Event data: %s' % str(event_json)) return HttpResponse('Sale not found', status=500) orders = Order.objects.filter(event=request.event, payment_provider='paypal', payment_info__icontains=sale['id']) order = None for o in orders: payment_info = json.loads(o.payment_info) for res in payment_info['transactions'][0]['related_resources']: for k, v in res.items(): if k == 'sale' and v['id'] == sale['id']: order = o break if not order: return HttpResponse('Order not found', status=200) order.log_action('pretix.plugins.paypal.event', data=event_json) if order.status == Order.STATUS_PAID and sale['state'] in ('partially_refunded', 'refunded'): RequiredAction.objects.create( event=request.event, action_type='pretix.plugins.paypal.refund', data=json.dumps({ 'order': order.code, 'sale': sale['id'] }) ) elif order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED) and sale['state'] == 'completed': try: mark_order_paid(order, user=None) except Quota.QuotaExceededException: if not RequiredAction.objects.filter(event=request.event, action_type='pretix.plugins.paypal.overpaid', data__icontains=order.code).exists(): RequiredAction.objects.create( event=request.event, action_type='pretix.plugins.paypal.overpaid', data=json.dumps({ 'order': order.code, 'payment': sale['parent_payment'] }) ) return HttpResponse(status=200)
def retry(request, order): try: order = Order.objects.current.get(user=request.user, code=order) except Order.DoesNotExist: return # TODO: Handle this provider = Paypal(order.event) provider.init_api() if "token" in request.GET: if "PayerID" in request.GET: payment = paypalrestsdk.Payment.find(request.session.get("payment_paypal_id")) provider._execute_payment(payment, request, order) else: messages.error(request, _("It looks like you cancelled the PayPal payment")) else: payment = paypalrestsdk.Payment( { "intent": "sale", "payer": {"payment_method": "paypal"}, "redirect_urls": { "return_url": build_absolute_uri("plugins:paypal:retry", kwargs={"order": order.code}), "cancel_url": build_absolute_uri("plugins:paypal:retry", kwargs={"order": order.code}), }, "transactions": [ { "item_list": { "items": [ { "name": "Order %s" % order.code, "quantity": 1, "price": str(order.total), "currency": order.event.currency, } ] }, "amount": {"currency": order.event.currency, "total": str(order.total)}, "description": __("Event tickets for %s") % order.event.name, } ], } ) resp = provider._create_payment(request, payment) if resp: return redirect(resp) return redirect( "presale:event.order", event=order.event.slug, organizer=order.event.organizer.slug, order=order.code )
def webhook(request, *args, **kwargs): event_body = request.body.decode('utf-8').strip() event_json = json.loads(event_body) # We do not check the signature, we just use it as a trigger to look the charge up. if event_json['resource_type'] not in ('sale', 'refund'): return HttpResponse("Not interested in this resource type", status=200) if event_json['resource_type'] == 'sale': saleid = event_json['resource']['id'] else: saleid = event_json['resource']['sale_id'] try: refs = [saleid] if event_json['resource'].get('parent_payment'): refs.append(event_json['resource'].get('parent_payment')) rso = ReferencedPayPalObject.objects.select_related( 'order', 'order__event').get(reference__in=refs) event = rso.order.event except ReferencedPayPalObject.DoesNotExist: rso = None if hasattr(request, 'event'): event = request.event else: return HttpResponse("Unable to detect event", status=200) prov = Paypal(event) prov.init_api() try: sale = paypalrestsdk.Sale.find(saleid) except: logger.exception('PayPal error on webhook. Event data: %s' % str(event_json)) return HttpResponse('Sale not found', status=500) if rso and rso.payment: payment = rso.payment else: payments = OrderPayment.objects.filter(order__event=event, provider='paypal', info__icontains=sale['id']) payment = None for p in payments: payment_info = p.info_data for res in payment_info['transactions'][0]['related_resources']: for k, v in res.items(): if k == 'sale' and v['id'] == sale['id']: payment = p break if not payment: return HttpResponse('Payment not found', status=200) payment.order.log_action('pretix.plugins.paypal.event', data=event_json) if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED and sale[ 'state'] in ('partially_refunded', 'refunded'): if event_json['resource_type'] == 'refund': try: refund = paypalrestsdk.Refund.find( event_json['resource']['id']) except: logger.exception('PayPal error on webhook. Event data: %s' % str(event_json)) return HttpResponse('Refund not found', status=500) known_refunds = { r.info_data.get('id'): r for r in payment.refunds.all() } if refund['id'] not in known_refunds: payment.create_external_refund( amount=abs(Decimal(refund['amount']['total'])), info=json.dumps(refund.to_dict( ) if not isinstance(refund, dict) else refund)) elif known_refunds.get(refund['id']).state in ( OrderRefund.REFUND_STATE_CREATED, OrderRefund. REFUND_STATE_TRANSIT) and refund['state'] == 'completed': known_refunds.get(refund['id']).done() if 'total_refunded_amount' in refund: known_sum = payment.refunds.filter( state__in=(OrderRefund.REFUND_STATE_DONE, OrderRefund.REFUND_STATE_TRANSIT, OrderRefund.REFUND_STATE_CREATED, OrderRefund.REFUND_SOURCE_EXTERNAL)).aggregate( s=Sum('amount'))['s'] or Decimal('0.00') total_refunded_amount = Decimal( refund['total_refunded_amount']['value']) if known_sum < total_refunded_amount: payment.create_external_refund( amount=total_refunded_amount - known_sum) elif sale['state'] == 'refunded': known_sum = payment.refunds.filter( state__in=(OrderRefund.REFUND_STATE_DONE, OrderRefund.REFUND_STATE_TRANSIT, OrderRefund.REFUND_STATE_CREATED, OrderRefund.REFUND_SOURCE_EXTERNAL)).aggregate( s=Sum('amount'))['s'] or Decimal('0.00') if known_sum < payment.amount: payment.create_external_refund(amount=payment.amount - known_sum) elif payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_CANCELED, OrderPayment.PAYMENT_STATE_FAILED ) and sale['state'] == 'completed': try: payment.confirm() except Quota.QuotaExceededException: pass return HttpResponse(status=200)
def webhook(request, *args, **kwargs): event_body = request.body.decode('utf-8').strip() event_json = json.loads(event_body) # We do not check the signature, we just use it as a trigger to look the charge up. if event_json['resource_type'] not in ('sale', 'refund'): return HttpResponse("Not interested in this resource type", status=200) if event_json['resource_type'] == 'sale': saleid = event_json['resource']['id'] else: saleid = event_json['resource']['sale_id'] try: refs = [saleid] if event_json['resource'].get('parent_payment'): refs.append(event_json['resource'].get('parent_payment')) rso = ReferencedPayPalObject.objects.select_related('order', 'order__event').get( reference__in=refs ) event = rso.order.event except ReferencedPayPalObject.DoesNotExist: rso = None if hasattr(request, 'event'): event = request.event else: return HttpResponse("Unable to detect event", status=200) prov = Paypal(event) prov.init_api() try: sale = paypalrestsdk.Sale.find(saleid) except: logger.exception('PayPal error on webhook. Event data: %s' % str(event_json)) return HttpResponse('Sale not found', status=500) if rso and rso.payment: payment = rso.payment else: payments = OrderPayment.objects.filter(order__event=event, provider='paypal', info__icontains=sale['id']) payment = None for p in payments: payment_info = p.info_data for res in payment_info['transactions'][0]['related_resources']: for k, v in res.items(): if k == 'sale' and v['id'] == sale['id']: payment = p break if not payment: return HttpResponse('Payment not found', status=200) payment.order.log_action('pretix.plugins.paypal.event', data=event_json) if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED and sale['state'] in ('partially_refunded', 'refunded'): if event_json['resource_type'] == 'refund': try: refund = paypalrestsdk.Refund.find(event_json['resource']['id']) except: logger.exception('PayPal error on webhook. Event data: %s' % str(event_json)) return HttpResponse('Refund not found', status=500) known_refunds = {r.info_data.get('id'): r for r in payment.refunds.all()} if refund['id'] not in known_refunds: payment.create_external_refund( amount=abs(Decimal(refund['amount']['total'])), info=json.dumps(refund.to_dict() if not isinstance(refund, dict) else refund) ) elif known_refunds.get(refund['id']).state in ( OrderRefund.REFUND_STATE_CREATED, OrderRefund.REFUND_STATE_TRANSIT) and refund['state'] == 'completed': known_refunds.get(refund['id']).done() if 'total_refunded_amount' in refund: known_sum = payment.refunds.filter( state__in=(OrderRefund.REFUND_STATE_DONE, OrderRefund.REFUND_STATE_TRANSIT, OrderRefund.REFUND_STATE_CREATED, OrderRefund.REFUND_SOURCE_EXTERNAL) ).aggregate(s=Sum('amount'))['s'] or Decimal('0.00') total_refunded_amount = Decimal(refund['total_refunded_amount']['value']) if known_sum < total_refunded_amount: payment.create_external_refund( amount=total_refunded_amount - known_sum ) elif sale['state'] == 'refunded': known_sum = payment.refunds.filter( state__in=(OrderRefund.REFUND_STATE_DONE, OrderRefund.REFUND_STATE_TRANSIT, OrderRefund.REFUND_STATE_CREATED, OrderRefund.REFUND_SOURCE_EXTERNAL) ).aggregate(s=Sum('amount'))['s'] or Decimal('0.00') if known_sum < payment.amount: payment.create_external_refund( amount=payment.amount - known_sum ) elif payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED) and sale['state'] == 'completed': try: payment.confirm() except Quota.QuotaExceededException: pass return HttpResponse(status=200)