I was trying to implement stripe in Django and everything worked fine until I tried to redirect the user to a success page after the payment. Can anybody have a look at my code and tell me what I am doing wrong?
views.py
@csrf_exempt
def create_checkout_session(request, id):
request_data = json.loads(request.body)
gig = get_object_or_404(Gig, pk=id)
stripe.api_key = settings.STRIPE_SECRET_KEY
checkout_session = stripe.checkout.Session.create(
customer_email=request_data['email'],
payment_method_types=['card'],
line_items=[
{
'price_data': {
'currency': 'eur',
'product_data': {
'name': gig.seller,
},
'unit_amount': int(gig.price * 100),
},
'quantity': 1,
}
],
mode='payment',
success_url='http://127.0.0.1:8000/checkout/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url='http://127.0.0.1:8000/checkout/failed/',
)
order = OrderDetail()
order.customer_email = request_data['email']
order.gig = gig
order.stripe_payment_intent = checkout_session.payment_intent
order.amount = int(gig.price * 100)
order.save()
# return JsonResponse({'data': checkout_session})
return JsonResponse({'sessionId': checkout_session.id})
class PaymentSuccessView(TemplateView):
template_name = "checkout/payment_success.html"
def get(self, request, *args, **kwargs):
session_id = request.GET.get('session_id')
if session_id is None:
return HttpResponse("failed")
stripe.api_key = settings.STRIPE_SECRET_KEY
session = stripe.checkout.Session.retrieve(session_id)
order = get_object_or_404(OrderDetail, stripe_payment_intent=session.payment_intent)
order.has_paid = True
order.save()
return render(request, self.template_name)
models.py
from django.db import models
from django.core import validators
class OrderDetail(models.Model):
id = models.BigAutoField(
primary_key=True
)
# You can change as a Foreign Key to the user model
customer_email = models.EmailField(
verbose_name='Customer Email'
)
gig = models.ForeignKey(
to=Gig,
verbose_name='Product',
on_delete=models.PROTECT
)
amount = models.IntegerField(
verbose_name='Amount'
)
stripe_payment_intent = models.CharField(
max_length=200, null=True, blank=True
)
# This field can be changed as status
has_paid = models.BooleanField(
default=False,
verbose_name='Payment Status'
)
created_on = models.DateTimeField(
auto_now_add=True
)
updated_on = models.DateTimeField(
auto_now_add=True
)
class Gig(models.Model):
id = models.BigAutoField(
primary_key=True
)
gigger = models.ForeignKey(
Mentors,
on_delete=models.CASCADE,
related_name="seller")
description = models.TextField(
blank=True,
max_length=800,
verbose_name='Description'
)
price = models.DecimalField(
verbose_name='Price',
decimal_places=2,
max_digits=6,
)
def __str__(self):
return f"{self.gigger}, ${self.price}, id:{self.id}"
class Comments:
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Commentor")
comment = models.TextField(blank=True)
stars = models.PositiveSmallIntegerField()
product.html
{% extends 'web/layout.html' %}
{% block body %}
<h1 >Product Detail</h1>
<div >
<div >
<div >
<h2>Product Detail</h2>
</div>
<div >
<div >
<div >
<img src="https://dummyimage.com/150x150.gif?text={{ object.name }}" alt="">
</div>
<div >
<h1>Name: {{ object.seller.mentor.username }}</h1>
<p>Description: {{ object.description }}</p>
<p>Price: {{ object.price }}</p>
<div >
<label for="email">Email: </label>
<input type="email" name="email" id="email" placeholder="{{object.gigger.mentor.email}}">
<small>Please enter your email address</small>
</div>
</div>
</div>
</div>
<div >
<button id="checkout-button">Checkout</button>
</div>
</div>
</div>
<script src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
// Create an instance of the Stripe object with your publishable API key
var stripe = Stripe('{{ stripe_publishable_key }}');
var checkoutButton = document.getElementById('checkout-button');
checkoutButton.addEventListener('click', function () {
var email = document.getElementById('email').value;
if (email.length == 0) {
alert("Please enter your email address.");
return;
}
// Create a new Checkout Session using the server-side endpoint you
// created in step 3.
fetch("http://127.0.0.1:8000/checkout/api/checkout-session/{{ object.id }}/", {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(
{ email: email }
)
})
.then(response => response.json())
.then(function (session) {
return stripe.redirectToCheckout({ sessionId: session.sessionId });
})
.then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, you should display the localized error message to your
// customer using `error.message`.
if (result.error) {
alert(result.error.message);
}
})
.catch(function (error) {
console.error('Error:', error);
});
});
</script>
{% endblock %}
After placing an order, when I go in the 'order details' object and see the 'stripe_payment_intend' key is always empty, it doesn't get saved. What else could I try to get each different order?
CodePudding user response:
The issue is that the point when you create the Checkout Session and set the order
data is disconnected from the point at which you render your success page with the PaymentSuccessView
class. Just because those two pieces of code are in the same file does not mean the state will be maintained between different requests.
What you can do instead is add the order information to the Checkout Session using metadata. Then when you retrieve the Checkout Session on your success page you can read the metadata
from the Checkout Session to get the associated information.