예제 #1
0
def donate(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = DonateForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            social_account = SocialAccount.objects.get(user=request.user)
            donator, created = Donator.objects.update_or_create(
                user=social_account)
            donator.save()
            donation = Donation(donator=donator,
                                amount=form.cleaned_data['amount'],
                                note=form.cleaned_data['note'])
            donation.save()
        return HttpResponseRedirect('/')

    # if a GET (or any other method) we'll create a blank form
    else:
        if request.user.is_superuser:
            form = DonateForm()
            donations = Donation.objects.all()
        else:
            form = DonateForm()
            social_account = SocialAccount.objects.get(user=request.user)
            donations = Donation.objects.filter(donator__user=social_account)

    return render(request, 'donation.html', {
        'form': form,
        'donations': donations
    })
예제 #2
0
def donate(request, animal_id):
    animal = get_object_or_404(Animal, pk=animal_id)
    print('before if statement')
    if request.POST:
        print('Hit if statement')
        wish = get_object_or_404(Wish, pk=request.POST['wish_id'])
        try:
            d = Donation(wish_id=request.POST['wish_id'],
                         first_name=request.POST['first_name'],
                         last_name=request.POST['last_name'],
                         email=request.POST['email'],
                         amount=request.POST['amount'])
            mailer.send_recpt(d)
            d.save()
            if wish.current_funding() >= wish.fund_amount:
                wish.complete_funding()
            # wish.save()
            print(f'{d.user} donated {d.amount} to {d.wish}')
        except (KeyError, Wish.DoesNotExist):
            return render(request, 'animals/detail.html', {
                'animal': animal,
                'error_message': 'Please select a wish'
            })
        else:
            # reverse() is a utility function provided by Django
            return HttpResponseRedirect(
                reverse('animals:detail', args=(animal.id, )))

    else:
        return HttpResponseRedirect(
            reverse('animals:detail', args=(animal.id, )))
예제 #3
0
    def setUp(self):
        self.request = HttpRequest()
        self.request.method = 'POST'

        # add the absolute url to be be included in email
        default_site = get_default_site()
        self.request.site = default_site
        self.request.META['HTTP_HOST'] = default_site.hostname
        self.request.META['SERVER_PORT'] = 443

        self.user = User.objects.create_user(
            email="*****@*****.**", password="******")
        self.user.first_name = 'Franky'
        self.user.last_name = 'Hung'
        # create test Donation
        self.donation = Donation(
            is_test=True,
            transaction_id="TEST-ABCDE12345",
            user=self.user,
            gateway=PaymentGateway.objects.get(title=GATEWAY_STRIPE),
            is_recurring=False,
            donation_amount=Decimal("10.00"),
            currency="HKD",
            guest_email='',
            payment_status=STATUS_COMPLETE,
            donation_date=datetime.now(timezone.utc),
        )
        # create test Subscription
        self.subscription = Subscription(
            is_test=True,
            profile_id='TEST-FGHIJ67890',
            user=self.user,
            gateway=PaymentGateway.objects.get(title=GATEWAY_STRIPE),
            recurring_amount=Decimal("10.00"),
            currency="HKD",
            recurring_status=STATUS_ACTIVE,
            subscribe_date=datetime.now(timezone.utc))
        # create test Renewal Donation
        self.renewal_donation = Donation(
            is_test=True,
            transaction_id="TEST-ABCDE12345",
            user=self.user,
            gateway=PaymentGateway.objects.get(title=GATEWAY_STRIPE),
            is_recurring=True,
            subscription=self.subscription,
            donation_amount=Decimal("10.00"),
            currency="HKD",
            guest_email='',
            payment_status=STATUS_COMPLETE,
            donation_date=datetime.now(timezone.utc),
        )
        # just set the subscription id to an arbitrary number
        # to prevent the django.urls.exceptions.NoReverseMatch error when using getFullReverseUrl in plain_texts.get_renewal_receipt_text
        self.renewal_donation.subscription.id = 1
예제 #4
0
def execute_frequent(request, pk=None):
    frequent = FrequentContribution.objects.get(id=pk)

    for member in frequent.member_set.all():
        donation = Donation(member=member,
                            amount=member.membership_fee,
                            date=frequent.execution_date,
                            arrived=True,
                            frequent_contribution=frequent)
        donation.save()

    return redirect('donation-list')
예제 #5
0
def add_donation(info, user, type):
    timestamp = info.get("timestamp", timezone.now())
    d = Donation(
        user = user,
        name = info['name'],
        comment = info['comment'],
        timestamp = timestamp,
        amount = info['donation_amount'],
        currency = info['currency'],
        type = type
    )
    primary_amount = currency_conversion(info['donation_amount'], info['currency'], TARGET_CURRENCY)
    if primary_amount:
        d.primary_amount = primary_amount
        d.primary_currency = TARGET_CURRENCY
    d.save()
    return d
예제 #6
0
    def handle(self, *args, **options):
        users = User.objects.filter(is_active=True)

        for user in users:
            # render e-mail template
            level = user.user_profile.level
            # don't send reminders for some levels
            if not level.send_donation_reminder:
                continue

            # check if there is already a registered donation from this user
            if Donation.objects.user_donations_this_month(user).count() > 0:
                continue

            # don't proceed if there is no e-mail for this user
            if not user.email:
                continue

            recommended_amount = level.recommended_donation
            context = {
                'user': user,
                'recommended_amount': recommended_amount,
                'level': level
                       }
            html = render_to_string('donations/donation_reminder.html', context)
            text = render_to_string('donations/donation_reminder.txt', context)
            subject = render_to_string(
                'donations/donation_reminder_subject.txt', context)
            # strip newlines
            subject = subject.strip()

            msg = EmailMultiAlternatives(subject, text,
                                         settings.DEFAULT_FROM_EMAIL,
                                         [user.email])
            msg.attach_alternative(html, 'text/html')
            msg.content_subtype = 'html'

            reminder_sent = False

            try:
                msg.send()
                reminder_sent = True
            except smtplib.SMTPException:
                pass

            d = Donation()
            d.amount = recommended_amount
            d.reminder_sent = reminder_sent
            d.user = user

            d.save()
            print("Sent reminder to: %s" % (user.email, ))
예제 #7
0
def add_donation(info, user, type):
    timestamp = info.get("timestamp", timezone.now())
    d = Donation(user=user,
                 name=info['name'],
                 comment=info['comment'],
                 timestamp=timestamp,
                 amount=info['donation_amount'],
                 currency=info['currency'],
                 type=type)
    primary_amount = currency_conversion(info['donation_amount'],
                                         info['currency'], TARGET_CURRENCY)
    if primary_amount:
        d.primary_amount = primary_amount
        d.primary_currency = TARGET_CURRENCY
    d.save()
    return d
예제 #8
0
    def process_webhook_response(self):
        initStripeApiKey()
        # Decide what actions to perform on Newstream's side according to the results/events from the Stripe notifications
        # Event: checkout.session.completed
        if self.event['type'] == EVENT_CHECKOUT_SESSION_COMPLETED:
            # Update payment status
            self.donation.payment_status = STATUS_COMPLETE
            # update donation_date
            self.donation.donation_date = datetime.now(timezone.utc)
            self.donation.save()

            # Since for recurring payment, subscription.updated event might lag behind checkout.session.completed
            if not self.donation.is_recurring:
                sendDonationReceiptToDonor(self.donation)
                sendDonationNotifToAdmins(self.donation)

            return HttpResponse(status=200)

        # Event: payment_intent.succeeded
        # Should be handled for onetime donations
        if self.event['type'] == EVENT_PAYMENT_INTENT_SUCCEEDED:
            # Update payment transaction_id as the charge id
            self.donation.transaction_id = self.payment_intent['charges'][
                'data'][0]['id']
            self.donation.save()

            return HttpResponse(status=200)

        # Event: invoice.created (for subscriptions, just return 200 here and do nothing - to signify to Stripe that it can proceed and finalize the invoice)
        # https://stripe.com/docs/billing/subscriptions/webhooks#understand
        if self.event['type'] == EVENT_INVOICE_CREATED and hasattr(
                self, 'subscription_obj') and hasattr(self, 'invoice'):
            return HttpResponse(status=200)

        # Event: invoice.paid (for subscriptions)
        if self.event['type'] == EVENT_INVOICE_PAID and hasattr(
                self, 'subscription_obj') and hasattr(self, 'invoice'):
            if self.invoice.status == 'paid':
                _debug("[stripe recurring] Invoice confirmed paid")
                # check if subscription has one or more invoices to determine it's a first time or renewal payment
                # self.subscription_obj here is the stripe subscription object
                try:
                    invoices = stripe.Invoice.list(
                        subscription=self.subscription_obj.id)
                except (stripe.error.RateLimitError,
                        stripe.error.InvalidRequestError,
                        stripe.error.AuthenticationError,
                        stripe.error.APIConnectionError,
                        stripe.error.StripeError) as e:
                    raise RuntimeError(
                        "Stripe API Error({}): Status({}), Code({}), Param({}), Message({})"
                        .format(
                            type(e).__name__, e.http_status, e.code, e.param,
                            e.user_message))
                # _debug("Stripe: Subscription {} has {} invoices.".format(self.subscription_obj.id, len(invoices['data'])))
                if len(invoices['data']) == 1:
                    _debug("[stripe recurring] First time subscription")
                    # save charge id as donation.transaction_id
                    self.donation.transaction_id = self.invoice.charge
                    self.donation.payment_status = STATUS_COMPLETE
                    self.donation.save()

                    # also save the invoice number as a DonationPaymentMeta
                    dpmeta = DonationPaymentMeta(
                        donation=self.donation,
                        field_key='stripe_invoice_number',
                        field_value=self.invoice.number)
                    dpmeta.save()
                elif len(invoices['data']) > 1:
                    _debug("[stripe recurring] About to add renewal donation")
                    # create a new donation record + then send donation receipt to user
                    # self.donation is the first donation made for a subscription
                    donation = Donation(
                        is_test=self.donation.is_test,
                        subscription=self.donation.subscription,
                        transaction_id=self.invoice.charge,
                        user=self.donation.user,
                        form=self.donation.form,
                        gateway=self.donation.gateway,
                        is_recurring=True,
                        donation_amount=formatDonationAmountFromGateway(
                            str(self.invoice.amount_paid),
                            self.donation.currency),
                        currency=self.donation.currency,
                        payment_status=STATUS_COMPLETE,
                        donation_date=datetime.now(timezone.utc),
                    )
                    donation.save()

                    dpmeta = DonationPaymentMeta(
                        donation=donation,
                        field_key='stripe_invoice_number',
                        field_value=self.invoice.number)
                    dpmeta.save()

                    # email notifications
                    sendRenewalReceiptToDonor(donation)
                    sendRenewalNotifToAdmins(donation)

                # log down the current subscription period span
                spmeta = SubscriptionPaymentMeta(
                    subscription=self.donation.subscription,
                    field_key='stripe_subscription_period',
                    field_value=str(self.subscription_obj.current_period_start)
                    + '-' + str(self.subscription_obj.current_period_end))
                spmeta.save()

                return HttpResponse(status=200)

        # Event: customer.subscription.updated
        if self.event[
                'type'] == EVENT_CUSTOMER_SUBSCRIPTION_UPDATED and hasattr(
                    self, 'subscription_obj'):
            # Subscription active after invoice paid
            if self.subscription_obj['status'] == 'active':
                if self.donation.subscription.recurring_status == STATUS_PROCESSING:
                    # save the new subscription, marked by profile_id
                    self.donation.subscription.profile_id = self.subscription_obj.id
                    self.donation.subscription.recurring_amount = formatDonationAmountFromGateway(
                        self.subscription_obj['items']['data'][0]['price']
                        ['unit_amount_decimal'], self.donation.currency)
                    self.donation.subscription.currency = self.donation.currency
                    self.donation.subscription.recurring_status = STATUS_ACTIVE
                    self.donation.subscription.save()

                    # set donation payment_status to complete(as this event may be faster than checkout.session.completed, for the email is sent next line)
                    self.donation.payment_status = STATUS_COMPLETE
                    self.donation.save()

                    # send the new recurring notifs to admins and donor as subscription is just active
                    sendNewRecurringNotifToAdmins(self.donation.subscription)
                    sendNewRecurringNotifToDonor(self.donation.subscription)
                else:
                    # check if pause_collection is marked_uncollectible
                    if self.subscription_obj[
                            'pause_collection'] and self.subscription_obj[
                                'pause_collection'][
                                    'behavior'] == 'mark_uncollectible':
                        self.donation.subscription.recurring_status = STATUS_PAUSED
                    else:
                        self.donation.subscription.recurring_status = STATUS_ACTIVE
                    self.donation.subscription.save()

                # price changes events should goes through the if-else block and returns 200 right here
                return HttpResponse(status=200)
            else:
                return HttpResponse(status=400)

        # Event: customer.subscription.deleted
        # self.donation is not initialized here, reason refer to Factory_Stripe.initGatewayByVerification
        if self.event[
                'type'] == EVENT_CUSTOMER_SUBSCRIPTION_DELETED and hasattr(
                    self, 'subscription_obj'):
            # update subscription recurring_status
            self.donation.subscription.recurring_status = STATUS_CANCELLED
            self.donation.subscription.save()

            # email notifications here because cancellation might occur manually at the stripe dashboard
            sendRecurringCancelledNotifToAdmins(self.donation.subscription)
            sendRecurringCancelledNotifToDonor(self.donation.subscription)

            return HttpResponse(status=200)

        # for other events:
        return HttpResponse(status=400)
예제 #9
0
    def process_webhook_response(self):
        # case one: donation is passed + not first_time_subscription = onetime donation
        if self.donation and not hasattr(self, 'first_time_subscription'):
            # change donation payment_status to 2c2p's payment_status, update recurring_status
            self.donation.payment_status = map2C2PPaymentStatus(
                self.data['payment_status'])
            self.donation.donation_date = datetime.now()
            self.donation.save()

            # email notifications
            if self.donation.payment_status == STATUS_REVOKED:
                sendDonationRevokedToDonor(self.donation)
                sendDonationRevokedToAdmins(self.donation)
            else:
                sendDonationReceiptToDonor(self.donation)
                sendDonationNotifToAdmins(self.donation)

            return HttpResponse(status=200)
        # case two: donation is passed + first_time_subscription: true
        if self.donation and self.first_time_subscription:
            # change donation payment_status to 2c2p's payment_status, update recurring_status
            self.donation.payment_status = map2C2PPaymentStatus(
                self.data['payment_status'])
            self.donation.save()

            if self.donation.payment_status == STATUS_COMPLETE:
                # create new Subscription object
                subscription = Subscription(
                    is_test=self.testing_mode,
                    profile_id=self.data['recurring_unique_id'],
                    user=self.donation.user,
                    gateway=self.donation.gateway,
                    recurring_amount=extract_payment_amount(
                        self.data['amount'], self.data['currency']),
                    currency=currencyCodeToKey(self.data['currency']),
                    recurring_status=STATUS_ACTIVE,
                    subscribe_date=datetime.now(timezone.utc))
                subscription.save()
                # link subscription to the donation
                self.donation.subscription = subscription
                self.donation.save()

                # send the donation receipt to donor and notification to admins if subscription is just created
                sendDonationReceiptToDonor(self.donation)
                sendDonationNotifToAdmins(self.donation)

                return HttpResponse(200)
            else:
                raise ValueError(
                    _("Cannot create subscription object due to donation payment_status: %(status)s"
                      ) % {'status': self.donation.payment_status})
        # case 3: renewals
        if not self.donation and self.subscription:
            # find the first donation made for this subscription
            fDonation = Donation.objects.filter(
                subscription=self.subscription).order_by('id').first()
            # Create new donation record from fDonation
            donation = Donation(
                is_test=self.testing_mode,
                subscription=self.subscription,
                transaction_id=self.data['order_id'],
                user=fDonation.user,
                form=fDonation.form,
                gateway=fDonation.gateway,
                is_recurring=True,
                donation_amount=extract_payment_amount(self.data['amount'],
                                                       self.data['currency']),
                currency=currencyCodeToKey(self.data['currency']),
                payment_status=map2C2PPaymentStatus(
                    self.data['payment_status']),
                donation_date=datetime.now(timezone.utc),
            )
            _debug('Save renewal Donation:' + self.data['order_id'])
            donation.save()

            # email notifications
            if donation.payment_status == STATUS_REVOKED:
                sendDonationRevokedToDonor(donation)
                sendDonationRevokedToAdmins(donation)
            else:
                sendRenewalReceiptToDonor(donation)
                sendRenewalNotifToAdmins(donation)

            return HttpResponse(200)
        else:
            raise RuntimeError(
                _("Unable to process_webhook_response after verifying 2C2P request"
                  ))
예제 #10
0
def confirm_donation(request):
    try:
        siteSettings = get_site_settings_from_default_site()
        tmpd = TempDonation.objects.get(
            pk=request.session.get('temp_donation_id', None))
        paymentMethod = displayGateway(tmpd)
        isGatewayHostedBool = isGatewayHosted(tmpd.gateway)
        if request.method == 'POST':
            # determine path based on submit-choice
            if request.POST.get('submit-choice', '') == 'change-submit':
                # goes back to step 1 which is donation details
                return redirect('donations:donate')
            elif request.POST.get('submit-choice', '') == 'confirm-submit':
                # proceed with the rest of the payment procedures
                # create processing donation
                transaction_id = gen_transaction_id(gateway=tmpd.gateway)
                donation = Donation(
                    is_test=tmpd.is_test,
                    transaction_id=transaction_id,
                    user=request.user
                    if request.user.is_authenticated else None,
                    form=tmpd.form,
                    gateway=tmpd.gateway,
                    is_recurring=tmpd.is_recurring,
                    donation_amount=tmpd.donation_amount,
                    currency=tmpd.currency,
                    guest_email=tmpd.guest_email
                    if not request.user.is_authenticated else '',
                    payment_status=STATUS_PROCESSING,
                    metas=temp_donation_meta_to_donation_meta(
                        tmpd.temp_metas.all()),
                    donation_date=datetime.now(timezone.utc),
                )
                # create a processing subscription if is_recurring
                if tmpd.is_recurring:
                    # create new Subscription object, with a temporary profile_id created by uuidv4
                    # user should have been authenticated according to flow logic
                    subscription = Subscription(
                        is_test=tmpd.is_test,
                        profile_id=uuid4_str(),
                        user=request.user
                        if request.user.is_authenticated else None,
                        gateway=tmpd.gateway,
                        recurring_amount=tmpd.donation_amount,
                        currency=tmpd.currency,
                        recurring_status=STATUS_PROCESSING,
                        subscribe_date=datetime.now(timezone.utc))
                    subscription.save()
                    # link subscription to the donation
                    donation.subscription = subscription

                donation.save()
                request.session.pop('temp_donation_id')
                # delete temp donation instead of saving it as processed
                tmpd.delete()
                # tmpd.status = STATUS_PROCESSED
                # tmpd.save()

                if 'first_time_registration' in request.session:
                    dpmeta = DonationPaymentMeta(
                        donation=donation,
                        field_key='is_user_first_donation',
                        field_value=request.session['first_time_registration'])
                    dpmeta.save()
                    request.session.pop('first_time_registration')

                # redirect to payment_gateway
                gatewayManager = InitPaymentGateway(request, donation=donation)
                return gatewayManager.redirect_to_gateway_url()
            else:
                raise Exception(
                    _('No valid submit-choice is being submitted.'))
    except TempDonation.DoesNotExist as e:
        messages.add_message(
            request, messages.ERROR,
            str(
                _('Session data has expired. Please enter the donation details again.'
                  )))
        return redirect('donations:donate')
    except Exception as e:
        # Should rarely happen, but in case some bugs or order id repeats itself
        _exception(str(e))
    return render(
        request, 'donations/confirm_donation.html', {
            'tmpd': tmpd,
            'paymentMethod': paymentMethod,
            'isGatewayHosted': isGatewayHostedBool
        })
예제 #11
0
    def process_webhook_response(self):
        # Event: EVENT_PAYMENT_CAPTURE_COMPLETED (This alone comes after the onetime donation is captured)
        if self.event_type == EVENT_PAYMENT_CAPTURE_COMPLETED:
            # update transaction_id
            self.donation.transaction_id = self.payload['id']
            self.donation.save()
            # payment should have been completed after successful capture at the moment of returning to this site
            # only run below code if somehow payment_status is still not complete(e.g. donor did not return to site)
            if self.donation.payment_status != STATUS_COMPLETE:
                self.donation.payment_status = STATUS_COMPLETE
                self.donation.save()

            # send email notifs
            sendDonationReceiptToDonor(self.donation)
            sendDonationNotifToAdmins(self.donation)

            return HttpResponse(status=200)

        # Event: EVENT_BILLING_SUBSCRIPTION_ACTIVATED
        if self.event_type == EVENT_BILLING_SUBSCRIPTION_ACTIVATED and hasattr(
                self, 'subscription_obj'):
            if self.subscription_obj['status'] == 'ACTIVE':
                if self.donation.subscription.recurring_status == STATUS_PROCESSING:
                    # save the new subscription, marked by profile_id
                    self.donation.subscription.profile_id = self.subscription_obj[
                        'id']
                    self.donation.subscription.recurring_amount = Decimal(
                        self.subscription_obj['billing_info']['last_payment']
                        ['amount']['value'])
                    self.donation.subscription.currency = self.subscription_obj[
                        'billing_info']['last_payment']['amount'][
                            'currency_code']
                    self.donation.subscription.recurring_status = STATUS_ACTIVE
                    self.donation.subscription.save()

                    # send the new recurring notifs to admins and donor as subscription is just active
                    sendNewRecurringNotifToAdmins(self.donation.subscription)
                    sendNewRecurringNotifToDonor(self.donation.subscription)

                return HttpResponse(status=200)
            else:
                raise ValueError(
                    _("EVENT_BILLING_SUBSCRIPTION_ACTIVATED but subscription status is %(status)s"
                      ) % {'status': self.subscription_obj['status']})

        # Event: EVENT_BILLING_SUBSCRIPTION_UPDATED
        if self.event_type == EVENT_BILLING_SUBSCRIPTION_UPDATED and hasattr(
                self, 'subscription_obj'):
            if self.subscription_obj[
                    'status'] == 'SUSPENDED' or self.subscription_obj[
                        'status'] == 'ACTIVE':
                subscription = Subscription.objects.filter(
                    profile_id=self.subscription_obj['id']).first()
                if not subscription:
                    raise ValueError(
                        _("Cannot find subscription object in database with profile_id %(id)s"
                          ) % {'id': self.subscription_obj['id']})
                subscription.recurring_amount = Decimal(
                    self.subscription_obj['plan']['billing_cycles'][0]
                    ['pricing_scheme']['fixed_price']['value'])
                subscription.save()

                return HttpResponse(status=200)
            else:
                raise ValueError(
                    _("EVENT_BILLING_SUBSCRIPTION_UPDATED but subscription status is %(status)s"
                      ) % {'status': self.subscription_obj['status']})

        # Event: EVENT_PAYMENT_SALE_COMPLETED
        if self.event_type == EVENT_PAYMENT_SALE_COMPLETED and hasattr(
                self, 'subscription_obj'):
            if self.payload['state'] == 'completed':
                # check if this is first time subscription payment or a renewal payment
                donationPMs = DonationPaymentMeta.objects.filter(
                    donation=self.donation, field_key='paypal_first_cycle')
                renewals = Donation.objects.filter(
                    subscription__profile_id=self.subscription_obj['id'])
                _debug("Number of donation PMs: " + str(len(donationPMs)))
                _debug("Number of renewals for subscription_id({}): ".format(
                    self.subscription_obj['id']) + str(len(renewals)))
                if len(donationPMs) == 1 or len(renewals) >= 2:
                    # this is already a renewal payment
                    # self.donation is the first donation associated with the subscription
                    if not self.donation.subscription:
                        raise ValueError(
                            _("Missing subscription linkage/object for donation %(id)s"
                              ) % {'id': self.donation.id})
                    donation = Donation(
                        is_test=self.donation.is_test,
                        subscription=self.donation.subscription,
                        transaction_id=self.payload['id'],
                        user=self.donation.user,
                        form=self.donation.form,
                        gateway=self.donation.gateway,
                        is_recurring=True,
                        donation_amount=Decimal(
                            self.payload['amount']['total']),
                        currency=self.payload['amount']['currency'],
                        payment_status=STATUS_COMPLETE,
                        donation_date=datetime.now(timezone.utc),
                    )
                    # save new donation as a record of renewal donation
                    donation.save()

                    # email notifications
                    sendRenewalReceiptToDonor(donation)
                    sendRenewalNotifToAdmins(donation)
                else:
                    # this is a first time subscription payment
                    self.donation.payment_status = STATUS_COMPLETE
                    self.donation.transaction_id = self.payload['id']
                    self.donation.save()

                    # save DonationPaymentMeta as proof of first time subscription payment
                    dpmeta = DonationPaymentMeta(
                        donation=self.donation,
                        field_key='paypal_first_cycle',
                        field_value='completed')
                    dpmeta.save()

                return HttpResponse(status=200)
            else:
                raise ValueError(
                    _("EVENT_PAYMENT_SALE_COMPLETED but payment state is %(state)s"
                      ) % {'state': self.payload['state']})

        # Event: EVENT_BILLING_SUBSCRIPTION_CANCELLED
        if self.event_type == EVENT_BILLING_SUBSCRIPTION_CANCELLED and hasattr(
                self, 'subscription_obj'):
            if self.subscription_obj['status'] == 'CANCELLED':
                self.donation.subscription.recurring_status = STATUS_CANCELLED
                self.donation.subscription.save()

                # email notifications
                sendRecurringCancelledNotifToAdmins(self.donation.subscription)
                sendRecurringCancelledNotifToDonor(self.donation.subscription)

                return HttpResponse(status=200)
            else:
                raise ValueError(
                    _("EVENT_BILLING_SUBSCRIPTION_CANCELLED but subscription status is %(status)s"
                      ) % {'status': self.subscription_obj['status']})

        # return 400 for all other events
        return HttpResponse(status=400)
예제 #12
0
    def process_webhook_response(self):
        if self.request.POST.get('txn_type', None) == 'subscr_payment':
            # copy what process_paypal_subscr_payment does in givewp to here
            # at this stage, Donation(parent) object is already populated from the custom param
            # Subscription object should exist also, since it is linked to Donation either via parent_payment_id
            # update profile_id if it's empty
            if not self.subscription.profile_id:
                self.subscription.profile_id = self.request.POST.get(
                    'subscr_id', None)
                self.subscription.save()

            transaction_id_dpm = ''
            try:
                # either it returns a record with empty field_value or not found(which raises the DoesNotExist exception)
                transaction_id_dpm = DonationPaymentMeta.objects.get(
                    donation=self.donation,
                    field_key='_give_payment_transaction_id')
            except DonationPaymentMeta.DoesNotExist as e:
                # will re-add transaction_id_dpm down below
                pass
            # parse 'payment_date' from ipn_data into python date object for comparison
            ipn_payment_date = self.request.POST.get('payment_date', None)
            pac_tz = pytz.timezone('US/Pacific')
            naive_date = re.sub(r'\s(PST|PDT)$', '', ipn_payment_date)
            ipn_payment_datetime = pac_tz.localize(
                datetime.strptime(naive_date, '%H:%M:%S %b %d, %Y'))
            ipn_payment_datetime_utc = ipn_payment_datetime.astimezone(
                pytz.utc)
            is_today = self.subscription.created_at.date(
            ) == ipn_payment_datetime_utc.date()

            # Look to see if payment is same day as sign up and we haven't set the transaction ID on the parent payment yet.
            if is_today and (not transaction_id_dpm or transaction_id_dpm
                             and not transaction_id_dpm.field_value):
                _debug("[paypal legacy recurring] is_today clause")
                # Verify the amount paid.
                initial_amount = round_half_up(
                    self.subscription.recurring_amount, 2)
                paid_amount = round_half_up(
                    self.request.POST.get('mc_gross', None), 2)

                if paid_amount < initial_amount:
                    # save DonationPaymentMeta as note of failure, but no need to save parent payment's status to failed
                    dpmeta = DonationPaymentMeta(
                        donation=self.donation,
                        field_key='IPN FAILURE',
                        field_value=str(
                            _('Payment failed due to invalid amount in PayPal Recurring IPN.'
                              )))
                    dpmeta.save()

                    raise Exception(
                        str(
                            _('Invalid payment amount in IPN subscr_payment response. IPN data: %(data)s'
                              ) %
                            {'data': json.dumps(self.request.POST.dict())}))

                # This is the very first payment so set the transaction ID.
                if transaction_id_dpm:
                    transaction_id_dpm.field_value = self.request.POST.get(
                        'txn_id', '')
                else:
                    transaction_id_dpm = DonationPaymentMeta(
                        donation=self.donation,
                        field_key='_give_payment_transaction_id',
                        field_value=self.request.POST.get('txn_id', ''))
                transaction_id_dpm.save()

                return HttpResponse(status=200)

            # Is this payment already recorded?
            try:
                transaction_id_dpm = DonationPaymentMeta.objects.get(
                    field_key='_give_payment_transaction_id',
                    field_value=self.request.POST.get('txn_id', ''))
                # Payment already recorded
                _debug("[paypal legacy recurring] payment already recorded")
                return HttpResponse(status=200)
            except DonationPaymentMeta.DoesNotExist as e:
                # continue code execution
                pass

            # add renewal payment to subscription
            # transaction_id uses the txn_id from paypal directly, just for convenience, value is the same as _give_payment_transaction_id in donationPaymentMeta
            renewal = Donation(
                is_test=self.testing_mode,
                subscription=self.donation.subscription,
                transaction_id=self.request.POST.get('txn_id')
                if self.request.POST.get('txn_id', '') else gen_transaction_id(
                    self.donation.gateway),
                user=self.donation.user,
                form=self.donation.form,
                gateway=self.donation.gateway,
                is_recurring=True,
                donation_amount=round_half_up(
                    self.request.POST.get('mc_gross', None), 2),
                currency=self.donation.currency,
                payment_status=STATUS_COMPLETE,
                donation_date=datetime.now(timezone.utc),
            )
            # save new donation as a record of renewal donation
            renewal.save()

            # email notifications
            sendRenewalReceiptToDonor(renewal)
            sendRenewalNotifToAdmins(renewal)

            # also save required DonationPaymentMetas, purchase key is skipped here as it should be saved at the parent payment/subscription
            transaction_id_dpm = DonationPaymentMeta(
                donation=renewal,
                field_key='_give_payment_transaction_id',
                field_value=self.request.POST.get('txn_id', ''))
            transaction_id_dpm.save()
            # skip out the renew method from givewp as we don't do that here

            return HttpResponse(status=200)
        elif self.request.POST.get('txn_type', None) == 'subscr_cancel':
            self.donation.subscription.recurring_status = STATUS_CANCELLED
            self.donation.subscription.save()

            # email notifications
            sendRecurringCancelledNotifToDonor(self.donation.subscription)
            sendRecurringCancelledNotifToAdmins(self.donation.subscription)

            return HttpResponse(status=200)
        else:
            _debug("[paypal legacy recurring] ipn txn_type not handled: " +
                   self.request.POST.get('txn_type', None))
            return HttpResponse(status=400)
예제 #13
0
    def handle(self, *args, **options):
        password = getpass("Enter %s password: "******"Connected to %s." % options['source_db'])
                donorids_list = []
                migrated_donations = 0
                with transaction.atomic():
                    with connection.cursor() as cursor:

                        if options['donorids']:
                            # migrate specific donors, beware it is a list of lists
                            donorids_list = [item for sublist in options['donorids'] for item in sublist]
                        else:
                            # migrate all donors
                            select_all_donors_query = "select id from wp_give_donors;"
                            cursor.execute(select_all_donors_query)
                            allDonors = cursor.fetchall()
                            donorids_list = [row[0] for row in allDonors]

                        total_donor_number = len(donorids_list)

                        # get source wordpress db timezone setting
                        timezone_query = "select option_value from wp_options where option_name = 'timezone_string';"
                        cursor.execute(timezone_query)
                        timezoneResult = cursor.fetchall()
                        timezone_string = timezoneResult[0][0]
                        sourcedb_tz = pytimezone(timezone_string)

                        for num, donor_id in enumerate(donorids_list, 1):
                            select_donor_lj_meta_query = "select id, email, name, date_created, dnm.* from wp_give_donors dn left join wp_give_donormeta dnm on dn.id = dnm.donor_id where dn.id = %d;" % donor_id
                            select_donor_donations_query = "select distinct donation_id from wp_give_donationmeta where meta_key = '_give_payment_donor_id' and meta_value = %d;" % donor_id
                            select_subscriptions_query = "select * from wp_give_subscriptions where customer_id = %d;" % donor_id
                            self.print("[%d/%d]...Now processing queries of givewp donor %d" % (num, total_donor_number, donor_id))
                            cursor.execute(select_donor_lj_meta_query)
                            donorMetaResult = cursor.fetchall()
                            cursor.execute(select_donor_donations_query)
                            donationsResult = cursor.fetchall()
                            donationids_list = [row[0] for row in donationsResult]
                            newUser = None
                            um = None

                            for i, row in enumerate(donorMetaResult):
                                givewp_donor_email = row[1]
                                givewp_donor_name = row[2]
                                givewp_donormeta_key = row[6]
                                givewp_donormeta_value = row[7]
                                if i == 0:
                                    # Add the user first
                                    newUser = User.objects.create_user(email=givewp_donor_email, password=uuid4_str())
                                    newUser.save()
                                    # save donor email as verified and primary
                                    email_obj = EmailAddress(email=givewp_donor_email, verified=True, primary=True, user=newUser)
                                    email_obj.save()
                                    # save donor's name attribute as UserMeta as I am not sure how to correctly split the name into first and last names
                                    um = UserMeta(user=newUser, field_key='_give_donor_name', field_value=givewp_donor_name)
                                    um.save()
                                if givewp_donormeta_key == '_give_donor_first_name':
                                    newUser.first_name = givewp_donormeta_value
                                elif givewp_donormeta_key == '_give_donor_last_name':
                                    newUser.last_name = givewp_donormeta_value
                                else:
                                    um = UserMeta(user=newUser, field_key=givewp_donormeta_key, field_value=givewp_donormeta_value)
                                    um.save()

                            newUser.save()
                            if options['verbose']:
                                self.print("[√] Created Newstream User (email: %s, name: %s)." % (newUser.email, newUser.fullname))

                            # add subscriptions
                            if options['verbose']:
                                self.print("...Now processing queries of subscriptions of givewp donor %d" % donor_id)
                            cursor.execute(select_subscriptions_query)
                            subscriptionsResult = cursor.fetchall()
                            newSubscription = None

                            for i, row in enumerate(subscriptionsResult):
                                # extract givewp subscription data
                                givewp_subscription_id = row[0]
                                givewp_subscription_initial_amount = row[4]
                                givewp_parent_donation_id = row[8]
                                givewp_subscription_created = row[10]
                                givewp_subscription_status = row[12]
                                subscription_profile_id = row[13]
                                # self.print("[info] givewp subscription id: %s" % givewp_subscription_id)
                                # self.print("[info] givewp subscription amount: %s" % givewp_subscription_initial_amount)
                                # self.print("[info] givewp subscription parent donation id: %s" % givewp_parent_donation_id)
                                # self.print("[info] givewp subscription created at: %s" % givewp_subscription_created)
                                # self.print("[info] givewp subscription status: %s" % givewp_subscription_status)
                                # self.print("[info] givewp subscription profile id: %s" % subscription_profile_id)
                                # query data for subscription's parent donation
                                parent_donation_query = "select * from wp_posts where ID = %d;" % givewp_parent_donation_id
                                cursor.execute(parent_donation_query)
                                parentDonationResult = cursor.fetchone()
                                if parentDonationResult is None:
                                    # donation_id might not exist in wp_posts, probably deleted
                                    # see https://givewp.com/documentation/core/donors/delete-donor
                                    self.print("[x] Could not obtain post %i" % givewp_parent_donation_id)
                                    # remove parentDonation id from donationids_list
                                    donationids_list.remove(givewp_parent_donation_id)
                                    # remove renewalDonation ids from donationids_list, query data for renewals of the subscription first
                                    renewals_query = "select distinct donation_id from wp_give_donationmeta where meta_key = 'subscription_id' and meta_value = %d;" % givewp_subscription_id
                                    cursor.execute(renewals_query)
                                    renewalsResult = cursor.fetchall()
                                    for renewalID in renewalsResult:
                                        donationids_list.remove(renewalID[0])
                                    continue
                                parent_donation_status = parentDonationResult[7]
                                parent_donation_datetime_local = sourcedb_tz.localize(parentDonationResult[2])
                                parent_donation_datetime = parent_donation_datetime_local.astimezone(pytz.utc)
                                # self.print("[info] givewp parent donation status: %s" % parent_donation_status)
                                # self.print("[info] givewp parent donation created at: %s" % parent_donation_datetime)
                                # query data for parent donation's meta data
                                parent_donationmeta_query = "select * from wp_give_donationmeta where donation_id = %d;" % givewp_parent_donation_id
                                cursor.execute(parent_donationmeta_query)
                                parentDonationMetaResult = cursor.fetchall()
                                parentDonationMetaDict = {}
                                for meta in parentDonationMetaResult:
                                    parentDonationMetaDict[meta[2]] = meta[3]
                                # query data for renewals of the subscription
                                renewals_query = "select distinct donation_id from wp_give_donationmeta where meta_key = 'subscription_id' and meta_value = %d;" % givewp_subscription_id
                                cursor.execute(renewals_query)
                                renewalsResult = cursor.fetchall()

                                newSubscription = Subscription(
                                    id=givewp_subscription_id,
                                    is_test=self.paymentmode_mapping(parentDonationMetaDict['_give_payment_mode']),
                                    profile_id=subscription_profile_id,
                                    user=newUser,
                                    gateway=self.gateway_mapping(parentDonationMetaDict['_give_payment_gateway']),
                                    recurring_amount=round_half_up(givewp_subscription_initial_amount, 2),
                                    currency=parentDonationMetaDict['_give_payment_currency'],
                                    recurring_status=self.subscription_status_mapping(givewp_subscription_status),
                                    subscribe_date=givewp_subscription_created.replace(tzinfo=timezone.utc)
                                )
                                newSubscription.save()
                                if options['verbose']:
                                    self.print("[√] Created Newstream Subscription (id: %d, profile_id: %s)" % (newSubscription.id, newSubscription.profile_id))

                                # add donations linked to this subscription(need to link with the new subscription id in Newstream)
                                # need to add the parent payment first, so it gets the smallest id among the renewals
                                parentDonation = Donation(
                                    id=givewp_parent_donation_id,
                                    is_test=newSubscription.is_test,
                                    subscription=newSubscription,
                                    transaction_id=parentDonationMetaDict['_give_payment_transaction_id'] if '_give_payment_transaction_id' in parentDonationMetaDict else gen_transaction_id(newSubscription.gateway),
                                    user=newUser,
                                    gateway=newSubscription.gateway,
                                    is_recurring=True,
                                    donation_amount=round_half_up(parentDonationMetaDict['_give_payment_total'], 2),
                                    currency=newSubscription.currency,
                                    payment_status=self.donation_status_mapping(parent_donation_status),
                                    donation_date=parent_donation_datetime,
                                )
                                parentDonation.save()
                                migrated_donations += 1
                                # remove parentDonation id from donationids_list
                                donationids_list.remove(givewp_parent_donation_id)
                                if options['verbose']:
                                    self.print("[√] Created Newstream Parent Donation (id: %d, amount: %s)" % (parentDonation.id, parentDonation.donation_amount))

                                # save all meta data as DonationPaymentMeta
                                for key, value in parentDonationMetaDict.items():
                                    # if newUser first name and last name empty, save once again
                                    if key == '_give_donor_billing_first_name' and not newUser.first_name:
                                        newUser.first_name = value
                                        newUser.save()
                                    if key == '_give_donor_billing_last_name' and not newUser.last_name:
                                        newUser.last_name = value
                                        newUser.save()
                                    dpm = DonationPaymentMeta(donation=parentDonation, field_key=key, field_value=value)
                                    dpm.save()
                                
                                # then add renewals as well
                                for renewalID in renewalsResult:
                                    # query data for renewal donation's meta data
                                    givewp_renewal_donation_id = renewalID[0]
                                    renewal_donation_query = "select * from wp_posts where ID = %d" % givewp_renewal_donation_id
                                    cursor.execute(renewal_donation_query)
                                    renewalDonationResult = cursor.fetchone()
                                    if renewalDonationResult is None:
                                        # donation_id fetched from wp_give_donationmeta might not exist in wp_posts, probably deleted
                                        # see https://givewp.com/documentation/core/donors/delete-donor
                                        self.print("[x] Could not obtain post %i" % givewp_renewal_donation_id)
                                        # remove renewalDonation id from donationids_list
                                        donationids_list.remove(givewp_renewal_donation_id)
                                        continue
                                    givewp_renewal_donation_status = renewalDonationResult[7]
                                    givewp_renewal_donation_datetime_local = sourcedb_tz.localize(renewalDonationResult[2])
                                    givewp_renewal_donation_datetime = givewp_renewal_donation_datetime_local.astimezone(pytz.utc)
                                    # self.print("[info] givewp renewal donation status: %s" % givewp_renewal_donation_status)
                                    # self.print("[info] givewp renewal donation created at: %s" % givewp_renewal_donation_datetime)
                                    renewal_donationmeta_query = "select * from wp_give_donationmeta where donation_id = %d;" % givewp_renewal_donation_id
                                    cursor.execute(renewal_donationmeta_query)
                                    renewalDonationMetaResult = cursor.fetchall()
                                    renewalDonationMetaDict = {}
                                    for meta in renewalDonationMetaResult:
                                        renewalDonationMetaDict[meta[2]] = meta[3]

                                    renewalDonation = Donation(
                                        id=givewp_renewal_donation_id,
                                        is_test=parentDonation.is_test,
                                        subscription=newSubscription,
                                        transaction_id=renewalDonationMetaDict['_give_payment_transaction_id'] if '_give_payment_transaction_id' in renewalDonationMetaDict else gen_transaction_id(self.gateway_mapping(renewalDonationMetaDict['_give_payment_gateway'])),
                                        user=newUser,
                                        gateway=self.gateway_mapping(renewalDonationMetaDict['_give_payment_gateway']),
                                        is_recurring=True,
                                        donation_amount=round_half_up(renewalDonationMetaDict['_give_payment_total'], 2),
                                        currency=renewalDonationMetaDict['_give_payment_currency'],
                                        payment_status=self.donation_status_mapping(givewp_renewal_donation_status),
                                        donation_date=givewp_renewal_donation_datetime,
                                    )
                                    renewalDonation.save()
                                    migrated_donations += 1
                                    # remove renewalDonation id from donationids_list
                                    donationids_list.remove(givewp_renewal_donation_id)
                                    if options['verbose']:
                                        self.print("[√] Created Newstream Renewal Donation (id: %d, amount: %s)" % (renewalDonation.id, renewalDonation.donation_amount))

                                    # save all meta data as DonationPaymentMeta
                                    for key, value in renewalDonationMetaDict.items():
                                        dpm = DonationPaymentMeta(donation=renewalDonation, field_key=key, field_value=value)
                                        dpm.save()

                            # loop remaining (one-time) donations from donationids_list
                            if options['verbose']:
                                self.print("...Now processing one-time donations of givewp donor %d" % donor_id)
                            for givewp_donation_id in donationids_list:
                                givewp_donation_query = "select * from wp_posts where ID = %d;" % givewp_donation_id
                                cursor.execute(givewp_donation_query)
                                givewpDonationResult = cursor.fetchone()
                                if givewpDonationResult is None:
                                    # donation_id fetched from wp_give_donationmeta might not exist in wp_posts, probably deleted
                                    # see https://givewp.com/documentation/core/donors/delete-donor
                                    self.print("[x] Could not obtain post %i" % givewp_donation_id)
                                    continue
                                givewp_donation_status = givewpDonationResult[7]
                                givewp_donation_datetime_local = sourcedb_tz.localize(givewpDonationResult[2])
                                givewp_donation_datetime = givewp_donation_datetime_local.astimezone(pytz.utc)
                                # self.print("[info] givewp (onetime) donation status: %s" % givewp_donation_status)
                                # self.print("[info] givewp (onetime) donation created at: %s" % givewp_donation_datetime)
                                # query data for givewp donation's meta data
                                givewp_donationmeta_query = "select * from wp_give_donationmeta where donation_id = %d;" % givewp_donation_id
                                cursor.execute(givewp_donationmeta_query)
                                givewpDonationMetaResult = cursor.fetchall()
                                givewpDonationMetaDict = {}
                                for meta in givewpDonationMetaResult:
                                    givewpDonationMetaDict[meta[2]] = meta[3]

                                # add donations linked to this subscription(need to link with the new subscription id in Newstream)
                                # need to add the parent payment first, so it gets the smallest id among the renewals
                                singleDonation = Donation(
                                    id=givewp_donation_id,
                                    is_test=self.paymentmode_mapping(givewpDonationMetaDict['_give_payment_mode']),
                                    transaction_id=givewpDonationMetaDict['_give_payment_transaction_id'] if '_give_payment_transaction_id' in givewpDonationMetaDict else gen_transaction_id(self.gateway_mapping(givewpDonationMetaDict['_give_payment_gateway'])),
                                    user=newUser,
                                    gateway=self.gateway_mapping(givewpDonationMetaDict['_give_payment_gateway']),
                                    is_recurring=False,
                                    donation_amount=round_half_up(givewpDonationMetaDict['_give_payment_total'], 2),
                                    currency=givewpDonationMetaDict['_give_payment_currency'],
                                    payment_status=self.donation_status_mapping(givewp_donation_status),
                                    donation_date=givewp_donation_datetime,
                                )
                                singleDonation.save()
                                migrated_donations += 1
                                if options['verbose']:
                                    self.print("[√] Created Newstream (onetime) Donation (id: %d, amount: %s)" % (singleDonation.id, str(singleDonation.donation_amount)))

                self.print('==============================')
                self.print("Total Migrated Donations: %d" % migrated_donations)
        
            # reset sequences for donations and subscriptions
            sequence_sql = django_connection.ops.sequence_reset_sql(no_style(), [Donation, Subscription])
            with django_connection.cursor() as cursor:
                for sql in sequence_sql:
                    cursor.execute(sql)
        except Exception as e:
            self.print(str(e))
            self.print("...rolling back previous changes.")
            traceback.print_exc()