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,
Related
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
]);
TLDR; I want to allow customers to place orders using the previously used/saved card of their choice in my app. This requires the ability to specify to Stripe "which" card (previously associated to a customer) to charge. This should be possible in theory according to the docs, but I'm having no luck in practice.
Alright... talk is cheap, here are the codes:
// pseudo code of a customer placing an order for the first time
var stripe = require("stripe")(".....");
(async function(userId, cardToken) {
// Create a Customer AND save the payment method
const customer = await stripe.customers.create({
source: cardToken.id, // e.g. 'tok_mastercard' (retrieved from stripe.js)
email: 'paying.user#example.com',
});
// Charge the Customer instead of the card:
const charge = await stripe.charges.create({
amount: 1000,
currency: 'usd',
customer: customer.id
});
// save customer id to DB
await setCustomerIdToDatabase(userId, customer.id);
// save Card ID (not the card token ID) to DB
await addCustomerCardToDatabase(userId, cardToken.card.id); // <= note cardToken".card".id e.g. card_kldlkns...
})(userId, cardToken /* <= provided by app*/);
So far so good. Stripe saved the card from the specified token as the default payment method for the newly created customer then charges it successfully. But then my pain begins...
// pseudo code of a customer placing subsequent orders (WITH NEW CARDS)
var stripe = require("stripe")(".....");
(async function(userId, cardToken) {
const customerId = await getCustomerIdFromDatabase(userId);
// Create a Customer AND save the payment method
await stripe.customers.createSource(
customerId,
{
source: cardToken.id, // e.g. 'tok_mastercard' (retrieved from stripe.js)
}
);
// Attempt to charge the newly added card instead the Customer:
const charge = await stripe.charges.create({
amount: 1000,
currency: 'usd',
customer: customerId,
source: cardToken.card.id // <= note cardToken".card".id e.g. card_kldlkns...
});
/////////ERROR TRHOWN: No such token: card_kldlkns...
///////// what? I just saved it with .customers.createSource and you said it was OK???
/// unreachable code
// save Card ID (not the card token ID) to DB
await addCustomerCardToDatabase(userId, cardToken.card.id); // <= note cardToken".card".id e.g. card_kldlkns...
})(userId, cardToken /* <= provided by app*/);
Fails miserably... and so does this
// pseudo code of a customer placing subsequent orders (WITH SAVED CARDS)
var stripe = require("stripe")(".....");
(async function(userId, savedCardId) {
const customerId = await getCustomerIdFromDatabase(userId);
// Attempt to charge the newly added card instead the Customer:
const charge = await stripe.charges.create({
amount: 1000,
currency: 'usd',
customer: customerId,
source: savedCardId // e.g card_kldlkns...
});
/////////ERROR TRHOWN: No such token: card_kldlkns...
///////// what??????
})(userId, savedCardId /* <= provided by app*/);
What am I not getting here? The docs clearly says Stripe wants the card.id and the customer.id back if I want to charge a specific card.
Any help is appreciated
Turns out this was not an issue at all.
There was a faulty condition is the codes that was calling the wrong stripe API method
Can anyone assist as to why this is not posting to booking/charge upon completion of input to the checkout pop up window?
The simple checkout example posts fine, I am new to js so I don't quite understand the flow of the commands.
<form action="/booking/charge" method="post">
<script src="https://checkout.stripe.com/checkout.js"></script>
<button id="customButton">Purchase</button>
<script>
var handler = StripeCheckout.configure({
key: 'pk_test_xxxxxxxxxxx',
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function(token) {
// You can access the token ID with `token.id`.
// Get the token ID to your server-side code for use.
}
});
document.getElementById('customButton').addEventListener('click', function(e) {
// Open Checkout with further options:
handler.open({
name: 'xxxx',
email: "test#test.com",
description: '2 widgets',
currency: 'gbp',
amount: 350
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
</form>
If you use the custom Checkout integration, you need to do a little more work. You write your own code to handle the token returned by Stripe. This is all done within token callback.
Here's a traditional form submission example, it uses JQuery, appends the token and user's email as values to hidden form elements, then submits the form.
function (token) {
// Use the token to create the charge with a server-side script.
$("#stripeToken").val(token.id);
$("#stripeEmail").val(token.email);
$("#myForm").submit();
}
Full Example: https://jsfiddle.net/osrLsc8m/
Alternatively, you could submit the data to your backend with an AJAX request.
function (token) {
var myData = {
token: token.id,
email: token.email
};
/*
Make an AJAX post request using JQuery,
change the first parameter to your charge script
*/
$.post("/echo/html/",myData,function(data){ ... });
}
Full Example: http://jsfiddle.net/742tety5/
Code that has worked for me (must include script for jQuery in header not footer)
<script src="https://checkout.stripe.com/checkout.js"></script>
<form id="myForm">
<input type="hidden" id="message" value="Hello, world"/></p>
<input type="hidden" id="amount" value="10.00" /></p>
<p><button type="submit" id="customButton">Pay</button></p>
</form>
<script>
// checkout handler
var handler = StripeCheckout.configure({
key: '<YOUR PUBLIC KEY>',
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
token: function(token) {
/* Use the token to create the charge with a server-side script.
You can access the token ID with `token.id`
Pass along various parameters you get from the token response
and your form.*/
var myData = {
token: token.id,
email: token.email,
amount: Math.round($("#amount").val() * 100),
message: $("#message").val()
};
/* Make an AJAX post request using JQuery,
change the first parameter to your charge script*/
$.post("<YOUR ROUTE TO charge.php", myData,function (data) {
// if you get some results back update results
$("#myForm").hide();
$(".results").html("Your charge was successful");
}).fail(function () {
// if things fail, tell us
$(".results").html("I'm sorry something went wrong");
})
}
});
$('#myForm').on('submit', function (e) {
// Open Checkout with further options
handler.open({
name: 'Stripe.com',
email: 'test#test.com',
description: '2 widgets',
amount: Math.round($("#amount").val() * 100)
});
e.preventDefault();
});
// Close Checkout on page navigation
$(window).on('popstate', function () {
handler.close();
});
</script>
Hope this is of help to someone, experiencing same issue.
I'm aware of the issue concerning handler.open in anything but a click event, due to browser popup blockers, but please see the following code I am using:
HTML
<button type="button" data-stripe data-image="/path/to/image.jpg">The button</button>
JS
export function paymentScreen(form, response) {
const restaurant = form.querySelector('[name="restaurant"]').value,
guests = form.querySelector('[name="guests"]').value,
time = form.querySelector('[name="time"]').value.split(':').slice(0, 2).join(':'),
email = form.querySelector('[name="email"]').value,
stripe = form.querySelector('[data-stripe]');
const handler = window.StripeCheckout.configure({
key: response.StripePublishableKey,
name: `Dim t - ${restaurant}`,
description: `Booking for ${guests} guests at ${time}`,
amount: parseInt(response.PaymentAmount, 10),
email: email,
currency: 'GBP',
locale: 'auto',
token(stripeResponse) {
stripePaymentProcess(form, response, stripeResponse);
}
});
// This part fails, even though we're in a click event listener.
stripe.addEventListener('click', function (e) {
handler.open({
image: this.dataset.image,
zipCode: false
});
});
}
The error I get in the console is as follows:
Stripe Checkout was unable to open a new window, possibly due to a
popup blocker. To provide the best experience for your users, follow
the guide at
https://stripe.com/docs/checkout#integration-more-runloop.
If I read the guide, I cannot see what I am doing wrong.
Any ideas on how to solve this? Thanks.
I am using Laravel 5.1 trying to set it up with Stripe using Cashier.
I am using a custom button to execute the javascript (using Angular):
$scope.subscribe = function(plan){
var handler = StripeCheckout.configure({
key: 'pk_test_*************',
image: '/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function(token) {
// Use the token to create the charge with a server-side script.
// You can access the token ID with `token.id`
var data = 'stripeToken=' + token.id;
$http.post("/createSubscription", data).success(function(data, status) {
console.log(data);
});
}
});
handler.open({
name: 'basic',
description: 'basic monthly $100',
currency: "usd",
amount: 10000
});
$(window).on('popstate', function() {
handler.close();
});
};
And in my Laravel code I have:
public function createSubscription(Request $request)
{
$user = JWTAuth::parseToken()->authenticate();
$token = Input::get('stripeToken');
$user->subscription("basic_plan")->create($token);
return 'test';
}
But I keep getting an error saying "this customer has no attached payment source".
The returned token at this line:
token: function(token) {
...
Does contain the users email, card info (+ card token), and stripe token. But when I check my Stripe dashboard, a customer is added without any data (no card, no email and not set up with the subscription).
I am trying to create customers with a subscription plan via this form.
(I do have the Billable trait set up and included in the controller)