Stripe Payment integration PHP JSON trouble with passing email through workflow - javascript

I am testing strip payment integration in PHP and downloaded the test files directly from the stripe. I want to be able to add "receipt_email" to the workflow. If I define it directly on the create.php and that works fine, but the trouble comes when I define it as a hidden input in the form on checkout.html I don't know how to read it into the create.php since the client.js is fetching the create.php
checkout.htm
<form id="payment-form">
<input type="hidden" id="email" value="email#domain.com">
<div id="card-element"><!--Stripe.js injects the Card Element--></div>
<button id="submit">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text">Pay</span>
</button>
<p id="card-error" role="alert"></p>
<p class="result-message hidden">
Payment succeeded, see the result in your
Stripe dashboard. Refresh the page to pay again.
</p>
</form>
client.js
// The items the customer wants to buy
var purchase = {
items: [{ id: "shirt" }]
};
var formData = {
'email': document.getElementById("email").value
};
// Disable the button until we have Stripe set up on the page
document.querySelector("button").disabled = true;
fetch("create.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(purchase) //I have tried using FormData Here but
need purchase also
})
.then(function(result) {
return result.json();
})
.then(function(data) {
var elements = stripe.elements();
create.php
function calculateOrderAmount(array $items): int {
// Replace this constant with a calculation of the order's amount
// Calculate the order total on the server to prevent
// customers from directly manipulating the amount on the client
return 1400;
}
header('Content-Type: application/json');
try {
// retrieve JSON from POST body
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str);
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => calculateOrderAmount($json_obj->items),
'currency' => 'usd',
'receipt_email' => ??, //ok if defined here but I need to pull it from
input in checkout.html
]);

Related

Fetch does not let Express.js/Node.js render a response from server-side to an EJS template in client-side

Purpose: Reading input (data) from the client-side, sending the data to the server-side (Node.js/Express.js), modifying the data on the server-side, and sending the modified data back to the client-side (EJS template)
Problem: When I use fetch() with the method set to POST, I can send data to the server-side, but I cannot render the modified data back to the client-side. However, if I use the HTML form directly with the method set to POST, I can achieve the goal (the purpose). However, I need to POST with fetch().
Research:
To narrow down the problem and especially check on the server-side code, I tried the following code without fetch(): I take the user's name from the input and send it to the server-side. On the server-side, I add a Hello and then return "Hello first_name last_name!" to an EJS variable named word:
The HTML form (in an ejs template):
<form action="/users" id="example-form" method="POST">
<label for="first-name">
<strong>First Name:</strong>
<input type="text" name="first_name" id="first-name">
</label>
<label for="last-name">
Last Name:
<input type="text" name="last_name" id="last-name">
</label>
<input type="submit" value="Create new user">
</form>
<br><hr><br>
<% var word %>
<h2><%= word %></h2>
The server-side code is:
//app.use(express.json())
app.post('/users', (req,res) => {
console.log(req.body)
res.render('index5',
{
word: `Hello ${req.body.first_name} ${req.body.last_name}!`
})
})
I commented out
//app.use(express.json())
and the code works without it.
Now, I remove method = "POST" from the HTML code. The following code is adopted from Simon Plenderleith's website:
HTML code:
<form action="/users" id="example-form">
<label for="first-name">
<strong>First Name:</strong>
<input type="text" name="first_name" id="first-name">
</label>
<label for="last-name">
Last Name:
<input type="text" name="last_name" id="last-name">
</label>
<input type="submit" value="Create new user">
</form>
I add the following to this HTML code and save them all in index5.ejs:
<br><hr><br>
<% var word %>
<h2><%= word %></h2>
I add the following JavaScript code from Simon Plenderleith's website to index5.ejs:
async function postFormDataAsJson({ url, formData }) {
const plainFormData = Object.fromEntries(formData.entries());
const formDataJsonString = JSON.stringify(plainFormData);
const fetchOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: formDataJsonString,
};
const response = await fetch(url, fetchOptions);
if (!response.ok) {
const errorMessage = await response.text();
throw new Error(errorMessage);
}
return response.json();
}
async function handleFormSubmit(event) {
event.preventDefault();
const form = event.currentTarget;
const url = form.action;
try {
const formData = new FormData(form);
const responseData = await postFormDataAsJson({ url, formData });
console.log({ responseData });
} catch (error) {
console.error(error);
}
}
const exampleForm = document.getElementById("example-form");
exampleForm.addEventListener("submit", handleFormSubmit);
</script>
This gives the following error message in the console (client-side Chrome dev tool):
SyntaxError: Unexpected token < in JSON at position 0
Also, in the console in the client-side we see we have received an empty object:
{}
We still do not see any Hello first_name last_name! (e.g. Hello John Smith!) on the client-side! So the code also failed to render back those strings to the client-side (index5.ejs)
In the above-given server code, I remove the comment // from app.use(express.json())
So, this is now the code on the server-side:
app.use(express.json())
app.post('/users', (req,res) => {
console.log(req.body)
res.render('index5',
{
word: `Hello ${req.body.first_name} ${req.body.last_name}!`
})
})
Now, we try John Smith again:
This time we have an improvement and we get this object in the console on the server-side:
{ first_name: 'John', last_name: 'Smith' }
We do not see Hello John Smith! on the client-side, so the server-side code fails to render it to the ejs variable word, when we use fetch() with the method set to POST. Remember, the server-side code worked with HTML form, when method = "POST" was set in the tag.
Also, I still get this error message in the console on the client-side (Chrome dev tool):
(index):68 SyntaxError: Unexpected token < in JSON at position 0
Using the Chrome dev tool, this error can be traced back to the following line of code in the above-given JavaScript code:
console.error(error);
Using Chrome dev tool under Networking/Fetch/XHR/Payload, I see the following:
{first_name: "John", last_name: "Smith"}
first_name: "John"
last_name: "Smith"
Seemingly, the above code with fetch() is successful in sending the data to the server-side but the following part of the server-side code fails:
res.render('index5',
{
word: `Hello ${req.body.first_name} ${req.body.last_name}!`
})
Clearly, I have to find a better understanding of how the data goes back and forth between the client-side and the server-side. While I continue working on this, I appreciate it if you could help me find out what I am missing and if you could suggest a quick fix for the above-given code.
Best regards,
Eric
Update May 4, 2022
If I change
return response.json()
in the JavaScript code (client-side) to
return response
then the error message in the console (client-side) disappears.This, however, does not solve the problem at hand: why with a regular form, node.js/express.js can render to an ejs template but with fetch() it cannot.

I am working on a django website and having trouble in calling a function at the correct place

I have a checkout form on my django website:<form class="row contact_form" action="." method="post" id="form">. That form has a submit button at the end:<input id="form-button" class="btnabc btnabc-outline-info btnabc-lg" type="submit" value="Continue to Payment">
On clicking this button I want to redirect the user to the payment gateway and if his transaction is successful I want to save his information that he entered in the form:
This is the javascript code for the button:
document.getElementById('payment-info').addEventListener('click', function (e) {
submitFormData()
})
function submitFormData() {
console.log('Payment Button Clicked')
var userFormData = {
'name': null,
}
var shippingInfo = {
'address': null,
}
shippingInfo.address = form.address.value
userFormData.name=form.name.value
var url = "/process_order/"
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body:JSON.stringify({'form': userFormData, 'shipping': shippingInfo }),
})
.then((response) => response.json())
.then((data) => {
console.log('Success:', data);
alert('Transaction Completed')
window.location.href = "{% url 'index' %}"
})
}
This is my views.py:
def processOrder(request):
transaction_id = datetime.datetime.now().timestamp()
data = json.loads(request.body)
if request.user.is_authenticated:
customer=request.user.customer
order, created=Order.objects.get_or_create(customer=customer, complete=False)
total=float(data['form']['total'])
order.transaction_id=transaction_id
if total == order.get_cart_total:
order.complete = True
order.save()
ShippingAddress.objects.create(
customer=customer,
order=order,
address=data['shipping']['address'],
city=data['shipping']['city'],
state=data['shipping']['state'],
zipcode=data['shipping']['zipcode'],
name=data['form']['name'],
email=data['form']['email'],
mobile=data['form']['mobile'],
)
param_dict = {
'MID': 'DIY12386817555501617',
'ORDER_ID': str(order.id),
'TXN_AMOUNT': '4',
'CUST_ID': 'j',
'INDUSTRY_TYPE_ID': 'Retail',
'WEBSITE': 'WEBSTAGING',
'CHANNEL_ID': 'WEB',
'CALLBACK_URL':'http://127.0.0.1:8000/handlerequest/',
}
param_dict['CHECKSUMHASH'] = Checksum.generate_checksum(param_dict, MERCHANT_KEY)
return render(request, 'paytm.html', {'param_dict': param_dict})
return HttpResponse('Done')
#csrf_exempt
def handlerequest(request):
# paytm will send you post request here
form = request.POST
response_dict = {}
for i in form.keys():
response_dict[i] = form[i]
if i == 'CHECKSUMHASH':
checksum = form[i]
verify = Checksum.verify_checksum(response_dict, MERCHANT_KEY, checksum)
if verify:
if response_dict['RESPCODE'] == '01':
print('order successful')
else:
print('order was not successful because' + response_dict['RESPMSG'])
return render(request, 'paymentstatus.html', {'response': response_dict})
This handlerequest function is basically for checking if the transaction is verified or not.
This is my paytm.html
<form action="https://securegw-stage.paytm.in/theia/processTransaction" method="post" name="paytm">
{% for key,value in param_dict.items %}
<input type="hidden" name="{{key}}" value="{{value}}">
{% endfor %}
</form>
</body>
<script>
document.paytm.submit()
</script>
Currently whenever I click on the payment button it just saves the information of the form (submitformdata() in javascript) without verifying the transaction and also i am not taken to the payment page where the user can pay.
The error is with the place where I am calling the submitformdata function and I cant figure out a way to call that function only when the transaction is verified. Please help me figuring out a way to do so. Any help would be appriciated.
actually the reason is when you try to implement this you haven't put this in the server right now so the paytm cant get access to the callback url for transaction verification just like i cant able to access 127.0.0.1:8000 from my laptop right now so you have to deploy that app so there is one thing you can do is use 'pagekite.net' and you get run temparary app on that url and make call back url accorging to that

json_decode($request->array) returns empty value on one of the object properties (image file) - array(0) { } {"photo":null}

Dynamic Form
Adding and removing boxes works fine and data is fed well upon entering text on form. Even the photo is being captured very well
<form>
<div class="box" v-for="(data,i) in datas" :key="i">
<input type="text" v-model="data[i]['key1']>
<input type="text" v-model="data[i]['key2']>
<input type="file" #change="selectImage($event,i)">
// I have not included add/delete methods to reduce codes
<button #click.prevent="method_to_add_box">Add</button>
<button #click.prevent="method_to_delete_box">Del</button>
<button #click.prevent="submitData">Submit</button>
</div>
</form>
Vuejs scripts
datas: [
{key1:'value1',key2:'value2',photoToUpload:null}
],
methods: {
submitData() {
let theData = new FormData();
theData.append('datas',JSON.stringify(this.datas));
let config = {
headers: {
'Content-Type':'multipart/form-data',
}
};
axios.post('/url', theData, config)
.then(res => console.log(res))
.catch(err => console.log(err))
},
selectImage(event, i) {
let theImage = event.target.files[0];
this.datas[i].photoToUpload = theImage;
}
}
I receive the data in Laravel backend
$datas = json_decode($request->datas);
if(is_array($datas)) {
foreach($datas as $data) {
$object = new Object();
$object->field1 = $data->key1;
$object->field2 = $data->key2;
// problem starts here - photo seen as an empty object so can't store()
$path = $data->photoToUpload->store('/path');
$path = explode('/',$path); $filename = $path[2]
$object->photo = $filename;
if($object->save()) {
return new ObjectResource($object);
}
}
}
Troubleshooting & Results
Tried to return response immediately after json_decode($request->datas) and here are the results;
$datas = json_decode($request->datas);
return \response([$datas[0]->photoToUpload]);
Developer Tools Network Tab response returns
[{ }], status is OK
have also tried returning extension of the photo
return \response([$datas[0]->photoToUpload->extension()]);
Developer Tools Network Tab preview returns message below
"Call to undefined method stdClass::extension()", Status Code: 500 Internal Server Error
However, if I don't put the fileToUpload inside an array in Vuejs data I can append to formdata, receive in Laravel backend and upload without troubles.
What am I doing wrong here? Please assist

How to access Symfony 3.4 object values from Request::createFromGlobals()

I am trying to use Symfony 3.4 components in a legacy project. There is a form that has two fields city and state. I want to send those two fields to a class method. So, I built an intermediate PHP file to pass the data to using javascript. The form and javascript are here.
<form class="form-inline" id="addpharmacies">
<div class="form-group">
<label for="city" ><?php print xlt("City");?></label>
<input type="text" class="form-control" id="city">
</div>
<div class="form-group">
<label for="state"><?php print xlt("State");?></label>
<input type="text" class="form-control" id="state">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" >Submit</button>
</div>
</form>
let f = document.getElementById('addpharmacies');
f.addEventListener('submit', importPharm);
function importPharm() {
top.restoreSession();
let city = document.getElementById("city").value;
let state = document.getElementById("state").value;
if (city.length === 0 || state.length ===0) {
alert("City and state must both be filled out")
return false;
}
let body = {"city": city, "state": state};
let req = new Request('pharmacyHelper.php', {method: "POST", body: body});
fetch(req)
.then(response=> {
if (response.status === 200) {
return response.json()
} else {
throw new Error("Bad response from the server");
}
})
.then(json => {
alert(json) // Not a great UI experience
})
}
As you can see that I am using a listener to submit the form by way of the javascript function importPharm. The javascript gets the data from the form via a getElementById call to city and state fields.
A request is created and the data is passed into the request and the helper file is called to pass the data. I have verified that something is being sent to the helper file. But I can see is [object Object] when I do a getContent().
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$content = $request->getContent();
file_put_contents("api.txt", $content);
When I try:
$request->request->get('city','');
I get nothing. I have tried every combination I could find on the Symfony Site
Any suggestion will be greatly appreciated.
Well you haven't specified any headers clarifying your content-type so it's ambiguous for Symfony to detect your content type and map them to the Parameter Bag (although it displays the content body) , try adding the :
Content-Type : application/x-www-form-urlencoded
header and send your form inputs as form-data and try again , else you will need to json_decode the content body and deal with it as an array or \StdClass object .
N.B : Using form-data your request would look like this
let body = "city="+city+"&state="+state;
let req = new Request('pharmacyHelper.php', {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-url-encoded',
'Accept': 'application/json'
},body: body});
fetch(req).then(response=> {
if (response.status === 200) {
return response.json()
} else {
throw new Error("Bad response from the server");
}
})
.then(json => {
alert(json) // Not a great UI experience
})

Clarification on functionality of the stripe token

So I'm currently in the final stages of designing a small online shop and I'm having a bit of difficulty understanding what the stripe token contains, how to pick it up on the node.js server and that kind of thing.
currently, my client-side code looks like this:
<div style="text-align: center;">
<form id="paymentForm" action="//httpbin.org/post" method="POST">
<input type="hidden" id="stripeToken" name="stripeToken" />
<input type="hidden" id="stripeEmail" name="stripeEmail" />
<input type="hidden" id="cartTotal" name="cartTotal" />
<input type="hidden" id="cartContents" name="cartContents" />
</form>
<p><input type="button" class="button" id="purchaseButton" value="チェックアウト"></p>
<script>
var totalCost = 0;
var totalCartLoad = "";
totalCost = localStorage.getItem('totalCartPrice');
totalCartLoad = localStorage.getItem('whatsInCart');
totalCartLoad = totalCartLoad.replace('undefined','');
totalCartLoad = '_____________________________________' + totalCartLoad;
var finalCartLoad = String(totalCartLoad); //convert it to a string for display
var handler = StripeCheckout.configure({
key: 'pk_test_6pRNASCoBOKtIshFeQd4XMUh',
token: function(token) {
$("#stripeToken").val(token.id);
$("#stripeEmail").val(token.email);
$("#cartTotal").val(totalCost);
$("#cartContents").val(finalCartLoad);
$("#paymentForm").submit();
}
});
$('#purchaseButton').on('click', function(e) {
// Open Checkout with further options
handler.open({
name: "チェックアウト",
description: finalCartLoad,
shippingAddress: true,
billingAddress: true,
zipCode: true,
allowRememberMe: true,
currency: 'JPY',
amount: totalCost
});
e.preventDefault();
});
// Close Checkout on page navigation
$(window).on('popstate', function() {
handler.close();
});
</script>
</div>
and my server code looks like this:
const stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
module.exports = (req) => {
// the token is generated by Stripe and POST'ed
// to the `action` URL in our form
const token = req.body.stripeToken;
// now we create a charge which returns a `promise`
// so we need to make sure we correctly handle
// success and failure, but that's outside of this
// function (and we'll see it later)
return stripe.charges.create({
// ensures we send a number, and not a string
amount: parseInt(process.env.STRIPE_COST, 10),
currency: process.env.STRIPE_CCY,
source: token,
description: process.env.STRIPE_DESCRIPTION, // 👈 remember to change this!
// this metadata object property can hold any
// extra private meta data (like IP address)
metadata: {},
});
}
However I am uncertain how to make sure that the details I need such as shipping address, customer email, product manifest and that kind of thing, which I have collected on my client, end up where I need it, on the invoice or somewhere in my account on stripe. I am also uncertain exactly how the charge is made (I know I need an app.js file to go with this as well, so I'd appreciate some pointers at this point cause its really been doing my head in.
The Token.id is what you want to use as the source when creating the Charge, and it looks like that's what you're doing, so you should be good to go from that side.
You should currently find the email at req.body.stripeEmail; in fact, you should find all of the following in req.body:
$("#stripeToken").val(token.id); // req.body.stripeToken
$("#stripeEmail").val(token.email); // req.body.stripeEmail
$("#cartTotal").val(totalCost); // req.body.cartTotal
$("#cartContents").val(finalCartLoad); // req.body.cartContents
In order to get the Shipping address, you'll need to pass those along too; you can find them in the args argument of the token() function, so you just need to pull what you need from there and send it along in your form as well.
var handler = StripeCheckout.configure({
key: 'pk_test_6pRNASCoBOKtIshFeQd4XMUh',
token: function(token) {
$("#stripeToken").val(token.id);
$("#stripeEmail").val(token.email);
$("#cartTotal").val(totalCost);
$("#cartContents").val(finalCartLoad);
$("#userShippingA").val(token.shippingAddress);
$("#userBillingA").val(token.billingAddress);
$("#paymentForm").submit();
}
});
return stripe.charges.create({
// ensures we send a number, and not a string
amount: parseInt(process.env.STRIPE_COST, 10),
currency: process.env.STRIPE_CCY,
source: token,
description: req.body.cartContents,
shippingAddress: req.body.shippingAddress,
billingAddress: req.body.billingAddress,
email: req.body.stripeEmail,

Categories