A_K
A_K

Reputation: 912

How to submit data for the address and payment details after PayPal payment is made

I have recently integrated a PayPal payment option to my project and everything is working fine for the amounts of the items in the cart except that after a payment is made the items remain in the cart and there is no payment notified to the backend.

My question is how to set a submit for the address and all the details made in the check out the template along with the paid amount to reflect in the backend.

Here is the views.py

class CheckoutView(View):
    def get(self, *args, **kwargs):
        try:
            order = Order.objects.get(user=self.request.user, ordered=False)
            form = CheckoutForm()
            context = {
                'form': form,
                'couponform': CouponForm(),
                'order': order,
                'DISPLAY_COUPON_FORM': True
            }
            shipping_address_qs = Address.objects.filter(
                user=self.request.user,
                address_type='S',
                default='True'
            )
            if shipping_address_qs.exists():
                context.update(
                    {'default_shipping_address': shipping_address_qs[0]})

            billing_address_qs = Address.objects.filter(
                user=self.request.user,
                address_type='B',
                default='True'
            )
            if billing_address_qs.exists():
                context.update(
                    {'default_billing_address': billing_address_qs[0]})

            return render(self.request, "checkout.html", context)
        except ObjectDoesNotExist:
            messages.info(self.request, "You do not have an active order")
            return redirect("core:checkout")

    def post(self, *args, **kwargs):
        form = CheckoutForm(self.request.POST or None)
        try:
            order = Order.objects.get(user=self.request.user, ordered=False)
            if form.is_valid():

                use_default_shipping = form.cleaned_data.get(
                    'use_default_shipping')
                if use_default_shipping:
                    print("Using the defualt shipping address")
                    address_qs = Address.objects.filter(
                        user=self.request.user,
                        address_type='S',
                        default=True
                    )
                    if address_qs.exists():
                        shipping_address = address_qs[0]
                        order.shipping_address = shipping_address
                        order.save()
                    else:
                        messages.info(
                            self.request, "No default shipping address available")
                        return redirect('core:checkout')
                else:
                    print("User is entering a new shipping address")
                    shipping_address1 = form.cleaned_data.get(
                        'shipping_address')
                    shipping_address2 = form.cleaned_data.get(
                        'shipping_address2')
                    shipping_province = form.cleaned_data.get(
                        'shipping_province')
                    shipping_country = form.cleaned_data.get(
                        'shipping_country')
                    shipping_postal_code = form.cleaned_data.get(
                        'shipping_postal_code')
                    shipping_phone_number = form.cleaned_data.get(
                        'shipping_phone_number')

                    if is_valid_form([shipping_address1, shipping_province, shipping_country, shipping_postal_code,
                                      shipping_phone_number]):
                        shipping_address = Address(
                            user=self.request.user,
                            street_address=shipping_address1,
                            apartment_address=shipping_address2,
                            province=shipping_province,
                            country=shipping_country,
                            postal_code=shipping_postal_code,
                            phone_number=shipping_phone_number,
                            address_type='S'
                        )
                        shipping_address.save()

                        order.shipping_address = shipping_address
                        order.save()

                        set_default_shipping = form.cleaned_data.get(
                            'set_default_shipping')
                        if set_default_shipping:
                            shipping_address.default = True
                            shipping_address.save()

                    else:
                        messages.info(
                            self.request, "Please fill in the required shipping address fields")

                use_default_billing = form.cleaned_data.get(
                    'use_default_billing')
                same_billing_address = form.cleaned_data.get(
                    'same_billing_address')

                if same_billing_address:
                    billing_address = shipping_address
                    billing_address.pk = None
                    billing_address.save()
                    billing_address.address_type = 'B'
                    billing_address.save()
                    order.billing_address = billing_address
                    order.save()

                elif use_default_billing:
                    print("Using the default billing address")
                    address_qs = Address.objects.filter(
                        user=self.request.user,
                        address_type='B',
                        default='True'
                    )
                    if address_qs.exists():
                        billing_address = address_qs[0]
                        order.billing_address = billing_address
                        order.save()
                    else:
                        messages.info(
                            self.request, "No default billing address available")
                        return redirect('core:checkout')
                else:
                    print("User is entering a new billing address")
                    billing_address1 = form.cleaned_data.get(
                        'billing_address')
                    billing_address2 = form.cleaned_data.get(
                        'billing_address2')
                    billing_province = form.cleaned_data.get(
                        'billing_province')
                    billing_country = form.cleaned_data.get(
                        'billing_country')
                    billing_postal_code = form.cleaned_data.get(
                        'billing_postal_code')
                    billing_phone_number = form.cleaned_data.get(
                        'billing_phone_number')

                    if is_valid_form([billing_address1, billing_province, billing_country, billing_postal_code,
                                      billing_phone_number]):
                        billing_address = Address(
                            user=self.request.user,
                            street_address=billing_address1,
                            apartment_address=billing_address2,
                            province=billing_province,
                            country=billing_country,
                            postal_code=billing_postal_code,
                            phone_number=billing_phone_number,
                            address_type='B'
                        )
                        billing_address.save()

                        order.billing_address = billing_address
                        order.save()

                        set_default_billing = form.cleaned_data.get(
                            'set_default_billing')
                        if set_default_billing:
                            billing_address.default = True
                            billing_address.save()

                    else:
                        messages.info(
                            self.request, "Please fill in the required billing address fields")
                        return redirect('core:checkout')

                payment_option = form.cleaned_data.get('payment_option')

                if payment_option == 'S':
                    return redirect('core:payment', payment_option='stripe')
                elif payment_option == 'P':
                    return redirect('core:payment', payment_option='paypal')
                else:
                    messages.warning(
                        self.request, "Invalid payment option selected")
                    return redirect('core:checkout')

        except ObjectDoesNotExist:
            messages.warning(self.request, "You do not have an active order")
            return redirect("core:order-summary")


class PaymentView(View):
    def get(self, request, payment_option):
        order = Order.objects.get(user=request.user, ordered=False)
        if order.billing_address:
            context = {
                'order': order,
                'DISPLAY_COUPON_FORM': False,
                'payment_method': payment_option,
            }
            return render(self.request, "payment.html", context)
        else:
            messages.warning(
                self.request, "You have not added a billing address")
            return redirect("core:checkout")

    # `source` is obtained with Stripe.js; see https://stripe.com/docs/payments/accept-a-payment-charges#web-create
    # -token
    def post(self, *args, **kwargs):
        order = Order.objects.get(user=self.request.user, ordered=False)
        token = self.request.POST.get('stripeToken')
        amount = int(order.grand_total() * 100)

        try:
            charge = stripe.Charge.create(
                amount=amount,  # cents
                currency="usd",
                source=token,
            )
            # create payment
            payment = Payment()
            payment.stripe_charge_id = charge['id']
            payment.user = self.request.user
            payment.amount = order.grand_total()
            payment.save()

            # assign the payment to the order

            order_items = order.items.all()
            order_items.update(ordered=True)
            for item in order_items:
                item.save()

            order.ordered = True
            order.payment = payment
            order.ref_code = create_ref_code()
            order.save()

            messages.success(self.request, "Your Order was Successful ! ")

---------------error messages------------------------------------

Here is the paypal payment page

{% if payment_method == 'stripe' %}
-----------------stripe payment-------------------
{% elif payment_method == 'paypal' %}
<head>
    <!-- Add meta tags for mobile and IE -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
</head>
        <body>
          <main class="mt-5 pt-4">
            <div class="container wow fadeIn" style="margin-top: 54px; text-align: center">
            <!-- Set up a container element for the button -->
            <div id="paypal-button-container"></div>

            <!-- Include the PayPal JavaScript SDK -->
            <script src="https://www.paypal.com/sdk/js?client-id=sb&currency=USD"></script>

            <script>
             var total='{{ order.grand_total|floatformat:2 }}'
                // Render the PayPal button into #paypal-button-container
                paypal.Buttons({

                    // Set up the transaction
                    createOrder: function(data, actions) {
                        return actions.order.create({
                            purchase_units: [{
                                amount: {
                                    value: parseFloat(total).toFixed(2)
                                }
                            }]
                        });
                    },

                    // Finalize the transaction
                    onApprove: function(data, actions) {
                        return actions.order.capture().then(function(details) {
                            submitFormData()
                        });
                    }

                }).render('#paypal-button-container');
            </script>
            </div>
          </main>
        </body>

{% endif %}

Upvotes: 0

Views: 680

Answers (3)

Chris K.
Chris K.

Reputation: 75

Been having similar issue…took me a long time to kinda figure this out (still not totally sure though)…

I believe you’re using the wrong code. For server side implementation, use this instead: https://developer.paypal.com/demo/checkout/#/pattern/server

Note: The post routes (createOrder, onApprove), let you add a request body with form data that you can send to your server. Also, I changed my code to work with axios (pretty straight forward). Included little snipped here..

      // Call your server to finalize the transaction
  onApprove: function(data, actions) {
    return axios.post('http://localhost:3900/api/paypal/orders/' + data.orderID + '/capture/', {
      firstName: 'Tom',
      lastName: 'Smith',
      email: '[email protected]'
    })
      .then(fun……….

On the server I use this one for the createOrder route https://developer.paypal.com/docs/checkout/reference/server-integration/set-up-transaction/

And this one for the onApprove route
https://developer.paypal.com/docs/checkout/reference/server-integration/capture-transaction/

You also need to download paypals sdk and set up a little file for your credentials. It’s copy/paste, just drop your clientId + clientSecret in.
https://developer.paypal.com/docs/checkout/reference/server-integration/setup-sdk/

Then on the server you can just save the form data to your db when you get the ok from pp that order has been captured.

Not totally sure if this is the correct way. Its been rather difficult to figure out…

Side note on IPN, I understand it’s not needed with the above approach. See answer from Preston PHX
Paypal and digital downloads with woocommerce: PDT or IPN? Or is there a tutorial on how to implement both?

Upvotes: 1

Lex
Lex

Reputation: 5014

As other answers mention you need IPN (Instant Payment Notification). Payment confirmations happen asyncronysly in paypal (there is time for them to review the payment for fruad detection and cancel it).

You'll need to pass an internal transaction id to paypal. Set up an ipn route endpoint on your system, which paypal will ping with a sucess message once the payments confirmed.

Details here https://developer.paypal.com/docs/api-basics/notifications/ipn/#

enter image description here

Upvotes: 2

Charles
Charles

Reputation: 641

You should use the django-paypal package to do that. When using Paypal IPN, paypal sends a signal to Django, which you then need to deal with. Using this will allow you to empty the cart etc.

Upvotes: 3

Related Questions