def confirm(request, aktivitet_id, aktivitet_date_id): if request.method != 'POST': return redirect('aktiviteter:signup_participants', aktivitet_id, aktivitet_date_id) try: aktivitet_date = AktivitetDate.objects.select_related( *default_select_related, ).prefetch_related( *default_prefetch_related, ).get(id=aktivitet_date_id) for validation_func in [require_accepts_signups, require_stripe_account]: response = validation_func(request, aktivitet_date) if response is not None: return response except AktivitetDate.DoesNotExist: raise Http404 signup_session = SignupSession(request, aktivitet_date) if not signup_session.is_valid(): return redirect('aktiviteter:signup_participants', aktivitet_date.aktivitet.id, aktivitet_date.id) # Create participant group participant_group = ParticipantGroup.objects.create( aktivitet_date=aktivitet_date, signee=request.user if not request.user.is_anonymous else None, comment=signup_session.comment, extras=signup_session.extras, participate_together=signup_session.get_participate_together(), ) # Signup questions questions = aktivitet_date.aktivitet.get_signup_questions() # Create participants, and add them to the participant group for participant in signup_session.participating_participants: db_participant = Participant( participant_group=participant_group, user=participant.user, first_name=participant.first_name, last_name=participant.last_name, email=participant.chosen_email, phone_mobile=participant.chosen_phone, birth_date=participant.birth_date, price=None, discount_code=participant.discount_code, state=participant.state, extras=participant.extras, ) db_participant.save() # If participant is related to a user (of any kind) and the user does # not have a birth_date, update user with participant birth_date if participant.user and not participant.user.get_birth_date(): participant.user.set_contact_info({'birth_date': participant.birth_date}) # Save answers for question in questions: answer = participant.get_answer_for_question(question.id) if answer: if question.field_type == 'select_multiple': answer = "\r\n".join(answer) SignupAnswer.objects.create( aktivitet_date=aktivitet_date, question=question, answer=answer, participant=db_participant, ) if aktivitet_date.payment_enabled: # Store the current price and payment method for all signed-up users for participant in participant_group.participants.all(): price = participant.calculate_price() participant.price = price['price'] participant.discount_amount = price['discount'] if participant.price == 0: # If there is no fee, set the payment method to free participant.payment_method = 'free' else: # Otherwise, set the payment method to online, but only for the participants who are paying now - leave # payment method for waitinglist participants undefined if participant.state == 'signed_up': participant.payment_method = 'online' participant.save() # If payment is actually due (ie. payment is enabled AND total price is > 0), handle Stripe online payment if signup_session.payment_due(): if not 'stripe_token' in request.POST: raise ValidationError token = request.POST['stripe_token'] # Description of the Stripe charge description = 'Deltakeravgift for "%s" (%s kl %s)' % ( aktivitet_date.aktivitet.title, aktivitet_date.start_date, aktivitet_date.start_time, ) # Extra data attached to the Stripe charge metadata = { 'Aktivitet': aktivitet_date.aktivitet.title, 'Aktivitet Dato': 'Fra %s kl %s, til %s kl %s' % ( aktivitet_date.start_date, aktivitet_date.start_time, aktivitet_date.end_date, aktivitet_date.end_time, ), 'Aktivitet ID': aktivitet_date.aktivitet.id, 'Aktivitet Dato ID': aktivitet_date.id, 'Deltakere': ', '.join([ '%s' % p.full_name for p in participant_group.participants.all() ]), } forening = aktivitet_date.aktivitet.get_forening_or_cabin_owner() stripe_account = forening.payment_auth()['stripe_user_id'] with transaction.atomic(): try: # Create and execute the Stripe charge charge = Charge.new_charge( stripe_account, request.user if not request.user.is_anonymous else None, token, signup_session.current_price(), description, metadata, ) except Exception: # Unexpected and unhandled error during Stripe charge creation. # This must be logged for further research (as opposed to # expected card charge errors). errmsg = ( "Feil ved oppretting av Stripe charge ved " "aktivitetspåmelding" ) logger.error( errmsg, exc_info=sys.exc_info(), extra={ 'request': request, 'aktivitet_date': aktivitet_date, 'participant_group': participant_group, } ) # Discard the signup and send the user back to try again. request.session['aktivitet.signup.%s.payment_failure' % aktivitet_date.id] = { 'error_display': "", 'error_message': "Betalingen kunne ikke gjennomføres.", } participant_group.delete() # cascade deletes related participants and stripe charge return redirect('aktiviteter:signup_summary', aktivitet_date.aktivitet.id, aktivitet_date.id) if not charge.was_success(): # Unsuccessful payment. If the problem is of a technical nature (not the user's fault), keep the # participants signed up and ask them to pay later. If not, discard the signup and send the user back to try # again. if charge.error_type == 'card_error': request.session['aktivitet.signup.%s.payment_failure' % aktivitet_date.id] = { 'error_display': charge.get_error_code_display(), 'error_message': charge.error_message, } participant_group.delete() # cascade deletes related participants and stripe charge return redirect('aktiviteter:signup_summary', aktivitet_date.aktivitet.id, aktivitet_date.id) # Create the payment-participants relation with transaction.atomic(): payment = ParticipantPayment.objects.create( payee=request.user if not request.user.is_anonymous else None, stripe_charge=charge, ) payment.participants = participant_group.participants.all() # The signup is now complete; delete the session so that it will be reset if user starts a new session signup_session.delete() participant_group.send_signup_receipts() return redirect( 'aktiviteter:signup_done', aktivitet_id=aktivitet_date.aktivitet.id, aktivitet_date_id=aktivitet_date.id, participant_group_id=participant_group.id, )
def renew_membership(request): if request.method != 'POST': return redirect('user:account') if 'stripeToken' not in request.POST: return redirect('user:account') description = "Fornying av medlemskap" if date.today() < membership_year_start()['public_date']: applicable_year = membership_year_start()['public_date'].year else: applicable_year = membership_year_start()['public_date'].year + 1 metadata = { 'Periode': 'Medlemsåret %s' % applicable_year, } if not request.user.has_family(): # Single member, pay their own invoice amount = request.user.payment.status['amount_due'] metadata['Medlemskap'] = request.user.membership_type()['name'] metadata['Medlem'] = '%s (medlemsnr: %s)' % (request.user.get_full_name(), request.user.memberid) request.user.clear_cache() elif request.user.family.relation == 'family': # Family membership, pay the invoice of the parent amount = request.user.payment.status['amount_due'] metadata['Medlemskap'] = 'Familiemedlemskap' metadata['Familie hovedmedlem'] = '%s (medlemsnr: %s)' % ( request.user.family.parent.get_full_name(), request.user.family.parent.memberid, ) metadata['Alle familiemedlemmer'] = ', '.join([ '%s (medlemsnr: %s)' % (u.get_full_name(), u.memberid) for u in request.user.family.all_members() ]) request.user.family.clear_cache() elif request.user.family.relation == 'household': # Household membership, each member has their own invoice. amount = request.user.family.payment_amount_due() metadata['Medlemskap'] = 'Husstandsmedlemskap' metadata['Medlemmer'] = ', '.join([ '%s (medlemsnr: %s, %s, %s)' % ( u.get_full_name(), u.memberid, u.membership_type()['name'], currency(u.payment.status['amount_due']), ) for u in request.user.family.payment_members() ]) request.user.family.clear_cache() charge = Charge.new_charge( Forening.get_cached_forening(id=Forening.DNT_CENTRAL_ID).payment_auth()['stripe_user_id'], request.user, request.POST['stripeToken'], amount, description, metadata, ) try: if not charge.was_success(): # Unsuccessful payment. Store the error message and display it on the account page request.session['membership.%s.payment_failure' % request.user.memberid] = { 'error_display': charge.get_error_code_display(), 'error_message': charge.error_message, } else: # Payment was succesful; save it to Focus if not request.user.has_family() or request.user.family.relation == 'family': request.user.payment.save_pending_payment(charge.charge_id, amount) else: for household_member in request.user.family.payment_members(): household_member.payment.save_pending_payment( charge.charge_id, household_member.payment.status['amount_due'], ) except: # Problem during pending payment registration - likely connection issue with Focus. # - Since the payment isn't stored in Focus, the user will not get the confirmation, so add an error message # explaining the situation. # - Manual followup is required by memberservice, so send email to memberservice with required information # - Log an error to be able to trace events, in case it's ever needed messages.error(request, 'payment_registration_failure') logger.error( "Feil ved lagring av medlemskapsfornyelse i Focus", exc_info=sys.exc_info(), extra={ 'request': request, 'amount': amount, 'metadata': metadata, 'charge': charge, }, ) try: context = { 'amount': amount, 'metadata': metadata, 'charge': charge, } message = render_to_string('common/focus/payment_registration_failure.txt', context, request=request) send_mail( "Fornying av medlemskap kunne ikke lagres i Focus", message, settings.DEFAULT_FROM_EMAIL, [settings.MEMBERSERVICE_EMAIL], ) except (SMTPException, SSLError, UnicodeEncodeError): # Jesus. Error upon error. Log it, hopefully devs will be able to follow up. logger.error( "Kunne ikke sende varselsepost til medlemsservice, beskjed må gis manuelt", extra={ 'request': request, 'message': message, 'amount': amount, 'metadata': metadata, 'charge': charge, }, ) finally: return redirect('%s#forny-medlemskap' % reverse('user:account'))
def confirm(request, aktivitet_date_id): if request.method != "POST": return redirect("aktiviteter:signup_participants", aktivitet_date_id) try: aktivitet_date = ( AktivitetDate.objects.select_related(*default_select_related) .prefetch_related(*default_prefetch_related) .get(id=aktivitet_date_id) ) for validation_func in [require_accepts_signups, require_stripe_account]: response = validation_func(request, aktivitet_date) if response is not None: return response except AktivitetDate.DoesNotExist: raise Http404 signup_session = SignupSession(request, aktivitet_date) if not signup_session.is_valid(): return redirect("aktiviteter:signup_participants", aktivitet_date.id) # Create participant group participant_group = ParticipantGroup.objects.create( aktivitet_date=aktivitet_date, signee=request.user, comment=signup_session.comment, participate_together=signup_session.get_participate_together(), ) # Create participants, and add them to the participant group for participant in signup_session.participating_participants: Participant.objects.create( participant_group=participant_group, user=participant.user, price=None, discount_code=participant.discount_code, use_guardian_contact_info=participant.use_guardian_contact_info, state=participant.state, ) if aktivitet_date.payment_enabled: # Store the current price and payment method for all signed-up users for participant in participant_group.participants.all(): price = participant.get_price_for_user() participant.price = price["price"] participant.discount_amount = price["discount"] if participant.price == 0: # If there is no fee, set the payment method to free participant.payment_method = "free" else: # Otherwise, set the payment method to online, but only for the participants who are paying now - leave # payment method for waitinglist participants undefined if participant.state == "signed_up": participant.payment_method = "online" participant.save() # If payment is actually due (ie. payment is enabled AND total price is > 0), handle Stripe online payment if signup_session.payment_due(): if not "stripe_token" in request.POST: raise ValidationError token = request.POST["stripe_token"] # Description of the Stripe charge description = 'Deltakeravgift for "%s" (%s kl %s)' % ( aktivitet_date.aktivitet.title, aktivitet_date.start_date, aktivitet_date.start_time, ) # Extra data attached to the Stripe charge metadata = { "Aktivitet": aktivitet_date.aktivitet.title, "Aktivitet Dato": "Fra %s kl %s, til %s kl %s" % (aktivitet_date.start_date, aktivitet_date.start_time, aktivitet_date.end_date, aktivitet_date.end_time), "Aktivitet ID": aktivitet_date.aktivitet.id, "Aktivitet Dato ID": aktivitet_date.id, "Deltakere": ", ".join( ["%s (%s)" % (p.user.get_full_name(), p.user.id) for p in participant_group.participants.all()] ), } forening = aktivitet_date.aktivitet.get_forening_or_cabin_owner() stripe_account = forening.payment_auth()["stripe_user_id"] with transaction.atomic(): # Create and execute the Stripe charge charge = Charge.new_charge( stripe_account, request.user, token, signup_session.current_price(), description, metadata ) if not charge.was_success(): # Unsuccessful payment. If the problem is of a technical nature (not the user's fault), keep the # participants signed up and ask them to pay later. If not, discard the signup and send the user back to try # again. if charge.error_type == "card_error": request.session["aktivitet.signup.%s.payment_failure" % aktivitet_date.id] = { "error_display": charge.get_error_code_display(), "error_message": charge.error_message, } participant_group.delete() # cascade deletes related participants and stripe charge return redirect("aktiviteter:signup_summary", aktivitet_date.id) # Create the payment-participants relation with transaction.atomic(): payment = ParticipantPayment.objects.create(payee=request.user, stripe_charge=charge) payment.participants = participant_group.participants.all() # The signup is now complete; delete the session so that it will be reset if user starts a new session signup_session.delete() participant_group.send_signup_receipts() return redirect("user:aktiviteter_show", participant_group.id)