Reputation: 912
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¤cy=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
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
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/#
Upvotes: 2
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