Search code examples
pythondjangostripe-paymentsdjango-oscarstripe.js

How to integrate Stripe payments gateway with Django Oscar?


I am trying to integrate the Stripe payment gateway to Django oscar for an e-commerce site which sells physical goods like groceries online.I use python 3.6.3, Django 2.0, Django-oscar 1.6, stripe 1.82.2.

Method 1:

So I followed this link in django-oscar groups:

https://groups.google.com/forum/#!searchin/django-oscar/handle_payment$20override%7Csort:date/django-oscar/Cr8sBI0GBu0/PHRdXX2uFQAJ

I have signed up for a stripe account and used my publishable key and test key to configure stripe.The problem is, when I try to pay using the button provided with label "Pay with Card",it collects my card information and then when I click the button, it shows "Some money will be debited from the card" like in this image: Image of Preview page

Then after I click the place order button,it shows me this: Image of confirmation page

Though I have paid using my card. I guess oscar doesn't seem to know that the payment has been done through stripe already?But I'm not sure how to solve this.

Method 2: I tried to use dj-stripe,found here:

https://github.com/dj-stripe/dj-stripe

But I read the whole documentation on https://dj-stripe.readthedocs.io/en/stable-1.0/ , it seems like I can use it only for products that need subscriptions, mine don't require subscribing and the docs for dj-stripe aren't fully complete.

I tried with the official django-oscar repo, the link is here: https://github.com/django-oscar/django-oscar-stripe , this repository is like five years old and I don't think it would be compatible to use with my version of Django oscar.

Method 3: I tried using the stripe.js and elements and created my form to accept cards:

< script src = "https://js.stripe.com/v3/" > < /script> <
  script >
  var stripe = Stripe('your_stripe_publishable_key');
var elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
var style = {
  base: {
    color: '#32325d',
    lineHeight: '18px',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '20px',
    '::placeholder': {
      color: '#aab7c4'
    }
  },
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'
  }
};

// Create an instance of the card Element.
var card = elements.create('card', {
  style: style
});

// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
card.addEventListener('change', function(event) {
  var displayError = document.getElementById('card-errors');
  if (event.error) {
    displayError.textContent = event.error.message;
  } else {
    displayError.textContent = '';
  }
});

// Create a source or display an error when the form is submitted.
var form = document.getElementById('payment-form');

form.addEventListener('submit', function(event) {
  event.preventDefault();

  stripe.createSource(card).then(function(result) {
    if (result.error) {
      // Inform the user if there was an error
      var errorElement = document.getElementById('card-errors');
      errorElement.textContent = result.error.message;
    } else {
      // Send the source to your server
      stripeSourceHandler(result.source);
    }
  });
});

function stripeSourceHandler(source) {
  // Insert the source ID into the form so it gets submitted to the server
  var form = document.getElementById('payment-form');
  var hiddenInput = document.createElement('input');
  var hiddenAmount = document.createElement('input');

  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'stripeSource');
  hiddenInput.setAttribute('value', source.id);
  form.appendChild(hiddenInput);

  hiddenAmount.setAttribute('type', 'hidden');
  hiddenAmount.setAttribute('name', 'amt');
  hiddenAmount.setAttribute('value', '{{ order_total.incl_tax|safe }}');
  form.appendChild(hiddenAmount);

  // Submit the form
  form.submit();
}

<
/script>
<form action="/charge/" method="post" id="payment-form">
  {% csrf_token % }
  <div class="form-row">
    <label for="card-element">
                Credit or debit card
            </label>
    <div id="card-element">
      <!-- A Stripe Element will be inserted here. -->
    </div>

    <!-- Used to display Element errors. -->
    <div id="card-errors" role="alert"></div>
  </div>
  <br>
  <!--<hr>-->
  <button class="btn btn-primary">Pay Now</button>
</form>

And in my python views.py file I create a stripe charge and also sources.

@csrf_exempt
def stripe_payment(request):
    user = request.user
    source_id = request.POST.get("stripeSource", None)

    amount = request.POST.get("amt", None)
    stripe.api_key = "your_test_key"
    customer = stripe.Customer.create(
        email=email,
        source=source_id,
    )
    # print("Customer ID: ", customer['id'])
    amt = float(amount) * 100
    # print("Amount:", int(amt))
    int_amt = int(amt)
    charge = stripe.Charge.create(
        amount=int_amt,
        currency='cad',
        customer=customer['id'],
        source=source_id,
    ) 

    return HttpResponseRedirect("/checkout/preview/")

Then I created a webhook in the stripe dashboard and linked it to my local url , every-time there is a response from stripe being sent through the web-hook, this url is hit.

@csrf_exempt
def demo_checkout(request):

    # Retrieve the request's body and parse it as JSON:
    event_json = json.dumps(json.loads(request.body), indent=4)
    # event_json = json.loads(request.body)

    # Do something with event_json
    print("Json event:", event_json)

    return HttpResponse(status=200)

As of now, I can track the various events or logs from my dashboard and the events like creating a customer, making a charge and the web-hook sending a response is working fine,but I can't figure out how can I complete the payment such that Django-oscar can also know that the payment is done and it doesn't show "No payment was required": Thank you page

I have tried all these methods,but it still doesn't work.I am open to using any other method which is suggested or an improvement to what I have done in any of the methods explained so far.I am new to django-oscar and an answer with some code and some explanation would be helpful.


Solution

  • I have found a way to integrate Stripe with Django Oscar, this is one of the easy ways to do it.

    1. Create a stripe account first from here:https://stripe.com/, you will get a publishable key and a secret key, you can view them after logging in into the stripe dashboard under Developers > API keys.

    2. In your django oscar code side. Fork the checkout app from oscar, add it to the INSTALLED_APPS+=get_core_apps(['checkout']).To know how to fork an app you can follow this link from the docs:https://django-oscar.readthedocs.io/en/latest/topics/customisation.html#fork-oscar-app

    3. Create a file called facade.py under checkout, copy the keys from your dashboard into the settings.py file and do the other changes as suggested in this link: Stripe payment gateway integration on the django oscar groups, it just happens to be titled wrong.Just follow this whole page and it's done.