0

I am trying to get a subscription form working on my site. I am using Stripe. I can send data to Stripe using their API. However, I'm not able to save the data on the server-side after receiving it from Stripe:

// In subscription.js:

$( function() {
    var customerEmail          = document.querySelector("[data-email]").getAttribute('data-email');
    var submissionURL          = document.querySelector("[data-redirect]").getAttribute('data-redirect');
    var stripeSubscriptionForm = document.getElementById('subscriptionId');
    var cardElement = elements.create("card", { style: style });

    // Mounting the card element in the template
    cardElement.mount("#card-element");

    stripeSubscriptionForm.addEventListener('submit', function(event){
        event.preventDefault();  // Before submitting, we need to send the data to our database too
        theForm        = event.target;  // getting the form; same as using getElementById, but using the event only
        theFormData    =  new FormData(theForm);

    stripe.createPaymentMethod( {   type            : 'card',                // this is what's sent to stripe
                                    card            : cardElement,
                                    billing_details : { email : customerEmail, }
                                },

                                )

                                .then( (result) => {                       // Once stripe returns the result
                                       // Building the data
                                       theFormData.append('card', cardElement);    
                                       theFormData.append('billing_details', customerEmail);
                                       theFormData.append('payement_method', result.paymentMethod.id);

                                       // Setting up the request
                                       const xhr = new XMLHttpRequest()    // Creating an XHR request object
                                       xhr.open(method, url);              // Creating a GET request

                                       // Setting up the header

                                       xhr.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest");  // Ajax header
                                       xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); // Ajax header
                                       xhr.setRequestHeader("X-CSRF-TOKEN", "{{ csrf_token }}");  // csrf_token

                                       xhr.onload = function() {           // After server sends response data back
                                           console.log('Got something back from server ')
                                           const response = xhr.response;  // 1) get the response object of the XHR request object

                                           if (xhr.status === 201 ){       // If the item is created
                                               const responseJson = JSON.parse(response)
                                               console.log('and json response: ', responseJson);  
                                           }
                                           else{
                                               console.log('httpRequest status: ', httpRequest.status);  // Let's see what's going on
                                           }
                                       }
                                       // Sending the form data
                                       xhr.send(theFormData);                         // Sending the xhr request


                                    });
});

Then, in my views.py:

def post(self, request, *args, **kwargs):
        ''' Receives the subscription form '''            
        subscriber_form       = SubscriberForm(request.POST)
        billing_address_form  = CompanyAddressForm(request.POST)
        subscription_form     = SubscriptionForm(request.POST)

        if subscriber_form.is_valid() and billing_address_form.is_valid() and subscription_form.is_valid():
            print ('forms are valid')
            billing_address               = billing_address_form.save()
            subscriber_instance           = subscriber_form.save(commit = False)
            subscriber_instance.address   = billing_address
            print ('######################################################')

            if request.META.get('CONTENT_TYPE', '').lower() == 'application/json' and len(request.body) > 0:
                print ('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
                try:
                    body_data = json.loads(request.body)
                    print (body_data)

                except Exception as e:
                    request_body = request.body

                    print (request_body)print ('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
            else:
                print ("request.META.get('CONTENT_TYPE', ''): ", request.META.get('CONTENT_TYPE', '') )

            ## Creating a customer in stripe: Need the data from the front-end:
            customer_stripe = stripe.Customer.create( payment_method   =  data.get('payment_method', None),
                                                      email            =  data.get('email', None),
                                                      invoice_settings =  { 'default_payment_method': data.get('payment_method', None), },
                                                    )

            ## Creating a subscription in stripe
            subscription = stripe.Subscription.create( customer = customer_stripe.id, items = [{'plan': plan_idx, }],  # plan_idx is the plan id from stripe
                                                       expand   = ['latest_invoice.payment_intent'] )

            form = SubscriptionForm(request.POST)            

            if form.is_valid():                
                subscription_instance = form.save()

            else:
                context['form']    = SubscriptionForm(request.POST)

                return render(request, self.template_name, context)


            ## Creating the same customer for ourselves
            user              = request.user 
            ...

            customer, created = Customer.objects.get_or_create(...)

            return render(request, reverse('dashboard') )
        else:
            ... # Re-render the forms with errors

            return render(request, self.template_name, context)

The problem is I get:

######################################################
request.META.get('CONTENT_TYPE', ''):  multipart/form-data; boundary=----WebKitFormBoundaryIeaE9GpJ2EXQBkZK

How do I access my data? None of my forms have image or files, but for even if my form is multipart/form-data, which I may need to in future to add headshot to the subscriber, how can I get the data using body_data = json.loads(request.body)?

Edit 1: So, I figured since I have accessed the request object to get my forms in the post method, may be I should first ready the request.body since Django is complaining that once you touch the request object, you cannot read the stream again. So, here is the modification:

 def post(self, request, *args, **kwargs):
    ''' Receives the subscription form '''
    request_body = request.body
    try:
        body_data = json.loads(request_body)

    except Exception as e:
        print ('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
        print (request_body)
        print ('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')

but this returns

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
request_body:  b'------WebKitFormBoundaryBBzJCNQS96b76EKx\r\nContent-Disposition: form-data; name="csrfmiddlewaretoken"\r\n\r\n0ryuWTIxVRPjDGe0c9Fxj7Sc3KeCmBtAkiFK5EAlriY0QtAmwo6ip\r\n------WebKitFormBoundaryBBzJCNQS96b76EKx\r\nContent-Disposition: form-data; name="name"\r\n\r\nABC\r\n------WebKitFormBoundaryBBzJCNQS96b76EKx\r\nContent-Disposition: form-data; name="email"\r\n\r\nemail@gmail.com\r\n...'
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

The question is why the exception is raised and what is this WebKitFormBoundary business?

Edit 2 So, when I print the POST request on my server-side, I do get the data:

def post(self, request, *args, **kwargs):
    ''' Receives the subscription form '''
    # request_body = request.body     
    print ('request_post: ', request.POST)
    print ('')

which returns the following in my console:

request_post:  <QueryDict: {'csrfmiddlewaretoken': ['OOPVcfGU2WfcPl3ld-Jx1L7XI1vSHfFhp0v1SIzQHpIyWrrhrH0d03HYnS7DdssZ'], 'name': ['ABC'], 'email': ['email@gmail.com'],...

But, when I post the request.body, I get:

request_body:  b'------WebKitFormBoundaryG87zmkiNu0fTWY0g\r\nContent-Disposition: form-data; name="csrfmiddlewaretoken"\r\n\r\nOOPVcfGU2WfcPl3ld-Jx1L7XI1vSHfFhp0v1SIzQHpIyWrrhrH0d03HYnS7DdssZ\r\n------WebKitFormBoundaryG87zmkiNu0fTWY0g\r\nContent-Disposition: form-data; name="name"\r\n\r\nABC\r\n------WebKitFormBoundaryG87zmkiNu0fTWY0g\r\nContent-Disposition: form-data; name="email"\r\n\r\nemail@gmail.com\r\n------WebKitFormBoundaryG87zmkiNu0fTWY0g\r\n...'

So, does this tell us that the data is making its way to the server-side, and there is a problem retrieving the data?

EarlyCoder
  • 1,213
  • 2
  • 18
  • 42
  • why are you sending your data with `GET` method and try to get it's `body` in your view? – mahyar Jun 07 '20 at 05:32
  • How do you say I used GET? I did look and I am using POST request – EarlyCoder Jun 07 '20 at 14:59
  • 1
    it's in your javascript code that says so `xhr.open(method, url); // Creating a GET request`. when you want to access data that has been posted to you.you do it by `variable = request.POST["name_of_data_field"]` or better version `variable = request.POST.get("name_of_data_field", None)` instead of `request.body` – mahyar Jun 08 '20 at 05:08
  • @Mahyar, you are correct that I should use .POST in my Django view. I found out why I should use .POST rather than .body. Generally, you would use .body to deal with JSON, as suggested by this answer `https://stackoverflow.com/a/37570142/3549766 ` If you can elaborate on why .POST method should be used in this particular case, then I'd suggest you create an answer and I'll mark it as the correct answer. This is because the reason in this case can help other people dealing with Ajax, JSON, formData. – EarlyCoder Jun 08 '20 at 17:43

1 Answers1

1

i think your problem is in the data that you post in your javascript code, you are not sending a json object to your server but a form object theFormData = new FormData(theForm);

if you want to post a json object you should first create a javascript object then use JSON.stringyfy method on it to create a json and send that json to your server

for example

sendData = {field1: "value1", field2: "value2"}

xhr.send(JSON.stringify(sendData));
mahyar
  • 539
  • 1
  • 4
  • 12