I am trying to use the Stripe API to create a payment form as detailed here:
https://stripe.com/docs/payments/integration-builder
I would like to send the amout (that the user is charged) from the front-end so have attempted to add it to the fetch request as shown below:
var purchase = {
//items: [{ id: "xl-tshirt", price: 400 }]
amount: 2000
};
// 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)
});
However the value (currently hardcoded at 2000) is not pulling through to the POST body successfully and the payment intent is failing. Below is the code I am using:
try {
// retrieve JSON from POST body
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str, false);
$paymentIntent = \Stripe\PaymentIntent::create([
//'amount' => calculateOrderAmount($json_obj->items),
'amount' => $json_obj['amount'],
'currency' => 'usd',
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
Any advice is much appreciated.
You are sending false here, that converts it into an object
$json_obj = json_decode($json_str, false);
Then you are trying to use it as an array here
'amount' => $json_obj['amount'],
Try using
'amount' => $json_obj->amount,
Or
$json_obj = json_decode($json_str, true);
without changing anything else.
Related
I am using Stripe quick start code found here https://stripe.com/docs/payments/quickstart
I have narrowed down the problem to the "items" array in the checkout.js not being recognized or not properly constructed or whatever in the create.php file.
My customers will only be purchasing one type of item at different dollar amounts so this function
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
// people from directly manipulating the amount on the client
return 1400;
}
is useless to me and the amount can just go into this
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => calculateOrderAmount($jsonObj->items),
'currency' => 'eur',
'automatic_payment_methods' => [
'enabled' => true,
],
]);
as the total.
I have tried replacing calculateOrderAmount($jsonObj->items) with array_values($$items)[0] as well as everything else I can think of. Stripe support has been of no help.
Thank you in advance
The HTML: The price is in cents from the "value"
<select id="item-options"">
<option value="">---- SELECT----</option>
<option value="9000">300 ITEMS - $90.00 USD</option>
<option value="8000">200 ITEMS - $80.00 USD</option>
<option value="5000">100 ITEMS - $50.00 USD</option>
<option value="3000">50 ITEMS - $30.00 USD</option>
<option value="1750">25 ITEMS - $17.50 USD</option>
<option value="800">10 ITEMS - $8.00 USD</option>
<option value="450">5 ITEMS - $4.50 USD</option>
<option value="100">1 ITEMS - $1.00 USD</option>
</select>
The full javascript:
var price = $('#item-options option:selected').val();
// This is your test publishable API key.
const stripe = Stripe("pk_test_xxxxxxxxxxx....");
// The items the customer wants to buy I have change this to (amount: price)
const items = [{ id: "xl-tshirt" }];
let elements;
initialize();
checkStatus();
document
.querySelector("#payment-form")
.addEventListener("submit", handleSubmit);
// Fetches a payment intent and captures the client secret
async function initialize() {
const { clientSecret } = await fetch("/create.php", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items }),
}).then((r) => r.json());
elements = stripe.elements({ clientSecret });
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-element");
}
async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
// Make sure to change this to your payment completion page
return_url: "http://localhost:4242/public/checkout.html",
receipt_email: document.getElementById("email").value,
},
});
// This point will only be reached if there is an immediate error when
// confirming the payment. Otherwise, your customer will be redirected to
// your `return_url`. For some payment methods like iDEAL, your customer will
// be redirected to an intermediate site first to authorize the payment, then
// redirected to the `return_url`.
if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message);
} else {
showMessage("An unexpected error occured.");
}
setLoading(false);
}
// Fetches the payment intent status after payment submission
async function checkStatus() {
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);
if (!clientSecret) {
return;
}
const { paymentIntent } = await stripe.retrievePaymentIntent(clientSecret);
switch (paymentIntent.status) {
case "succeeded":
showMessage("Payment succeeded!");
break;
case "processing":
showMessage("Your payment is processing.");
break;
case "requires_payment_method":
showMessage("Your payment was not successful, please try again.");
break;
default:
showMessage("Something went wrong.");
break;
}
}
// ------- UI helpers -------
function showMessage(messageText) {
const messageContainer = document.querySelector("#payment-message");
messageContainer.classList.remove("hidden");
messageContainer.textContent = messageText;
setTimeout(function () {
messageContainer.classList.add("hidden");
messageText.textContent = "";
}, 4000);
}
// Show a spinner on payment submission
function setLoading(isLoading) {
if (isLoading) {
// Disable the button and show a spinner
document.querySelector("#submit").disabled = true;
document.querySelector("#spinner").classList.remove("hidden");
document.querySelector("#button-text").classList.add("hidden");
} else {
document.querySelector("#submit").disabled = false;
document.querySelector("#spinner").classList.add("hidden");
document.querySelector("#button-text").classList.remove("hidden");
}
}
The php :
<?php
require 'vendor/autoload.php';
// This is your test secret API key.
\Stripe\Stripe::setApiKey('sk_test_XXXXXX.........');
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
// people from directly manipulating the amount on the client
return 1400;
}
header('Content-Type: application/json');
try {
// retrieve JSON from POST body
$jsonStr = file_get_contents('php://input');
$jsonObj = json_decode($jsonStr);
// Create a PaymentIntent with amount and currency
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => calculateOrderAmount($jsonObj->items),
'currency' => 'eur',
'automatic_payment_methods' => [
'enabled' => true,
],
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
To prevent price manipulation on the client side you should pass to your PHP script ids of products, not the prices directly. By knowing the product ids, you can search for prices on the server side. That's what the calculateOrderAmount function is for in your example code.
Since you intended to sell only one type of product, the price of which will depend on the volume, you may want to send a number of items from the client. Then take a look at tiered pricing and make the code match your pricing model.
I solved this by doing a var_dump($items) in the php file and the console.log showed it as a stdClass Object. Using "$amount = $items[0]->amount;" in the "function calculateOrderAmount(array $items)" now gives me the correct price for the "$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => calculateOrderAmount($jsonObj->items),"
Thank you Justin for responding to my question.
I am trying to integrate a Stripe payment method in a web application. I am stuck: payment_init.php does not load, when I am redirected to the page. I get 403 Forbidden error code ("Forbidden. You don't have permission to access this resource.
Additionally, a 400 Bad Request error was encountered while trying to use an ErrorDocument to handle the request").
Here is my payment_init.php file's code:
<?php
// Include the Stripe PHP library
require_once 'stripe-php/init.php';
// Include the configuration file
require_once 'config.php';
$chosenService = $_POST['submitService'];
printf($chosenService);
// Product Details
if ($chosenService === "1") {
$productName = "Hajvágás (6900 HUF)";
$productID = "hc001";
$productPrice = 6900;
} elseif ($chosenService === "2") {
$productName = 'Hajvágás + Szakáll (9900 HUF)';
$productID = "hc002";
$productPrice = 9900;
};
$currency = "huf";
$description = "20% előleg Mobil Barber";
printf($productName);
// Set API key
\Stripe\Stripe::setApiKey(STRIPE_API_KEY);
$response = array(
'status' => 0,
'error' => array(
'message' => 'Invalid Request!'
)
);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$input = file_get_contents('php://input');
$request = json_decode($input);
}
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode($response);
exit;
}
if (!empty($request->createCheckoutSession)) {
printf($productName);
// Convert product price to cent
$stripeAmount = round($productPrice * 100, 2);
// Create new Checkout Session for the order
try {
printf($productName);
$checkout_session = \Stripe\Checkout\Session::create([
'line_items' => [[
'price_data' => [
'currency' => $currency,
'unit_amount' => $productPrice,
'product_data' => [
'name' => $productName,
'description' => $description,
],
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => STRIPE_SUCCESS_URL . '?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => STRIPE_CANCEL_URL,
]);
} catch (Exception $e) {
$api_error = $e->getMessage();
}
if (empty($api_error) && $checkout_session) {
$response = array(
'status' => 1,
'message' => 'Checkout Session created successfully!',
'sessionId' => $checkout_session->id
);
} else {
$response = array(
'status' => 0,
'error' => array(
'message' => 'Checkout Session creation failed! ' . $api_error
)
);
}
}
// Return response
echo json_encode($response);
When I print $chosenService and $productName variables outside the "if (!empty($request->createCheckoutSession)) {...}" condition, I get the parameters, so they are not NULL. But inside the condition I do not get anything back, neither NULL (does this mean that the $request is empty?). I even checked the Logs in Stripe dashboard, this is the err message there:
"parameter_missing - line_items[0][price_data][product_data][name]
Looks like you are missing the name field tied to a given line item's product_data.
This field is required in that it contains the name that will show up on associated invoice line item descriptions."
I would be really grateful, if someone could help me with this. Thank you in advance.
I don't think your problem is the $productName. I tested out the code you provided and it looks like the issue has to do with the price value. You convert the $productPrice to $stripeAmount but then you don't use it. Without the conversion the amounts for either of the services are less than the $0.50 threshold (with the USD = HUF conversion).
As their docs point out, Stripe requires your charge amounts to be valued between $0.50 and $999,999.00
I don't think this impacted your attempt here but it might also be worth updating the way in which you are invoking/using the Stripe PHP library to conform to the current standard: https://github.com/stripe/stripe-php#getting-started
It will mean you can more easily use the code snippets displayed in the API docs
UPDATED I built a pricing page that uses Stripe Checkout to use both a One-Time payment button for product 1 and a Subscription payment button for product 2.
My goal is to redirect the one time payment button to Stripe Checkout with a one time payment, and separately redirect the subscription payment to a checkout with a recurring payment.
According to STRIPE this can be done using Subscription as the Mode in the CheckoutSession in create-checkout-session.php (sample project) :
The mode of the Checkout Session. Required when using prices or setup
mode. Pass subscription if the Checkout Session includes at least one
recurring item.
Contrary to the Stripe Docs the following line of code: 'mode' => 'subscription', activates subscription payments ONLY, but it doesnt redirect one time payments. For one-time payments to work I must change it to: 'mode' => 'payment', but then subscription payments don't work.
Here's the php code in question:
<?php
require_once 'shared.php';
$domain_url = $config['domain'];
// Create new Checkout Session for the order
// Other optional params include:
// [billing_address_collection] - to display billing address details on the page
// [customer] - if you have an existing Stripe Customer ID
// [payment_intent_data] - lets capture the payment later
// [customer_email] - lets you prefill the email input in the form
// For full details see https://stripe.com/docs/api/checkout/sessions/create
// ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
$checkout_session = \Stripe\Checkout\Session::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'mode' => 'subscription',
'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]]
]);
echo json_encode(['sessionId' => $checkout_session['id']]);
And here's the javascript code:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId) {
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId
})
}).then(function(result) {
return result.json();
});
};
// Handle any errors returned from Checkout
var handleResult = function(result) {
if (result.error) {
var displayError = document.getElementById("error-message");
displayError.textContent = result.error.message;
}
};
/* Get your Stripe publishable key to initialize Stripe.js */
fetch("./config.php")
.then(function(result) {
return result.json();
})
.then(function(json) {
var publishableKey = json.publishableKey;
var subscriptionPriceId = json.subscriptionPrice;
var onetimePriceId = json.onetimePrice;
var stripe = Stripe(publishableKey);
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("subscription-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(subscriptionPriceId).then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("onetime-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(onetimePriceId).then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
});
Is it even possible to have both one time payments and recurring payments on the same page with Stripe Checkout? How can I accomplish this?
Update according to Bemn:
$checkout_session = \Stripe\Checkout\Session::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'mode' => $body->mode
'line_items' => [[
'price' => $body->price_xxx,
// For metered billing, do not pass quantity
'quantity' => 1,
]],
'line_items' => [[
'price' => $body->price_zzz,
// For metered billing, do not pass quantity
'quantity' => 1,
]]
]);
echo json_encode(['sessionId' => $checkout_session['id']]);
And the JS:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId, mode) {
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
mode: mode // <-- passing the mode, e.g. 'payment' or 'subscription'
})
}).then(function(result) {
return result.json();
});
};
And the HTML:
<div data-stripe-priceid="pricexxx" data-stripe-mode="payment" id="onetime-btn" class="bold mt-2 d-inline-block w-100-after-md max-width-xxs py-2 btn btn-secondary">Ore Time</div>
<div data-stripe-priceid="pricexxx" data-stripe-mode="subscription" id="subscription-btn" class="bold mt-2 d-inline-block w-100-after-md max-width-xxs py-2 btn btn-secondary">Ore Time</div>
Is it even possible to have both one time payments and recurring payments on the same page with Stripe Checkout?
Yes. The key is you should pass the correct options to generate the corresponding Stripe Checkout session ID.
How can I accomplish this?
Backend: Have a function to accept Stripe's price ID and payment mode as input and return a Stripe Checkout session ID as the output.
Frontend: Pass payment mode information to /create-checkout-session.php. (see the Note if you are unable to do so)
Details
The following solution assuming that:
You generate a Stripe Checkout Session ID at the backend. That ID will then pass to .createCheckoutSession() in js frontend.
You have a 1-time product (let's call it PAY) and a recurrent subscription (let's call it SUB).
Frontend
I think you are close. What you need to do is passing the mode information to your API endpoint as well:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId, mode) { // <-- add a mode parameter
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
mode: mode // <-- passing the mode, e.g. 'payment' or 'subscription'
})
}).then(function(result) {
return result.json();
});
};
If so, each checkout button in the page should have corresponding info of the priceId and payment mode. You can do so by storing them using data attribute:
<div data-stripe-priceid="price_yyy" data-stripe-mode="subscription">Recurrent</div>
<div data-stripe-priceid="price_zzz" data-stripe-mode="payment">1-time</div>
If so, you can get the data attributes by e.g. a click event.
Note: If you cannot add an extra param to indicate mode, you neeed to identify if the given price ID is a 1-time or recurrent product in the backend. See https://stripe.com/docs/api/prices/object?lang=php#price_object-type for more details.
Backend
Here are 2 sample code snippets from Stripe's documentation. Direct copying of them does not work.
Reference for PAY: https://stripe.com/docs/checkout/integration-builder
$checkout_session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'price_data' => [
'currency' => 'usd',
'unit_amount' => 2000,
'product_data' => [
'name' => 'Stubborn Attachments',
'images' => ["https://i.imgur.com/EHyR2nP.png"],
],
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => $YOUR_DOMAIN . '/success.html',
'cancel_url' => $YOUR_DOMAIN . '/cancel.html',
]);
In your case, you may not need to define 'price_data'. Instead, you should use 'price', like the next example.
Reference for SUB: https://stripe.com/docs/billing/subscriptions/checkout#create-session
$checkout_session = \Stripe\Checkout\Session::create([
'success_url' => 'https://example.com/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://example.com/canceled.html',
'payment_method_types' => ['card'],
'mode' => 'subscription',
'line_items' => [[
'price' => $body->priceId,
// For metered billing, do not pass quantity
'quantity' => 1,
]],
]);
Take a look at this reference: https://stripe.com/docs/api/checkout/sessions/create. For line_items, you can just simply using 'price' and pass the price ID (e.g. price_xxx), which means your 'line_items' will looks like this:
'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]],
For 'mode', use the value from your API request. It should be something like:
'mode' => $body->mode
Which means in your backend you better define a function (e.g. generate_checkout_session) to:
parse the json body received in the API request
get priceId and mode from the parsed data
use the priceId and mode in \Stripe\Checkout\Session::create and
returns the checkout_session ID
Hope this (and the reference urls) can help you.
When you create your Session you can pass both a Price for the recurring amount charged on the subscription and another Price for the one-time fee you want to charge. You can combine multiple recurring Prices and one-time Prices overall.
$checkout_session = \Stripe\Checkout\Session::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'mode' => 'subscription',
'line_items' => [
// Add a one-time Price for $10
[
'price' => 'price_123',
'quantity' => 1,
],
// Add another one-time Price for $23
[
'price' => 'price_345',
'quantity' => 1,
],
// Add a recurring Price for $100 monthly
[
'price' => 'price_ABC',
'quantity' => 1,
],
]);
The code above will create a session with 3 line items. One for $100 monthly, one for $10 just once and one for $23 just once. The total for the session would be $133 on the first payment. It will also start a subscription for $100 a month and future invoices will be for $100 as expected.
What I have got is that you just need to add a check for either it is one-time or subscription here is what you can do:
JS FILE CHANGES:
var createCheckoutSession = function(priceId, $mode) {
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
paymentType: $mode, // This vary based on the button clicked either one-time or subscription.
})
}).then(function(result) {
return result.json();
});
};
/* Get your Stripe publishable key to initialize Stripe.js */
fetch("./config.php")
.then(function(result) {
return result.json();
})
.then(function(json) {
var publishableKey = json.publishableKey;
var subscriptionPriceId = json.subscriptionPrice;
var onetimePriceId = json.onetimePrice;
var stripe = Stripe(publishableKey);
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("subscription-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(subscriptionPriceId, 'subscription').then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("onetime-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(onetimePriceId, 'onetime').then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
});
Now we need to make changes in PHP file:
PHP FILE CHANGES:
$checkout_session = \Stripe\Checkout\Session::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'mode' => $body->paymentType, // Here is what we have got from front-end
'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]]
]);
For subscription we actually need to set interval, which is not need to set in one time purses. Probably this error happening for this reason.
Add recurring can be solve recurring error.
recurring: {
interval: 'month' // 'month' | 'year'
}
I am using the PaymentIntent API to integrate Stripe payments using stripe-php SDK and Stripe.js V3.
Following This guide https://stripe.com/docs/payments/payment-intents/migration#saving-cards-checkout. I am getting what successful payments in my Stripe Dashboard done with test cards which do not require 3d-secure. But The Stripe's new SCA 3d secure Popup(according to their docs.) is not popping up, Which leads payments done with 3dsecure ENABLED cards to "Incomplete Payments" Tab in stripe Dashboard.
I have examine the code thoroughly multiple times and tested. I have noticed that my code skips(somtimes) OR throws an error "Unexpected end of JSON input" in the "Fetch Part" on the client side code.. which leads the 3d-secure cards payments to be incomplete.The JavaScript Fetch function is not fetching the "payment_method_id" from the specified file(url).
My Payment.js File:
var elements = stripe.elements();
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
var cardNumber = elements.create('cardNumber', {
style: style
});
cardNumber.mount('#cardNumber');
var cardExpiry = elements.create('cardExpiry', {
style: style
});
cardExpiry.mount('#cardExpiry');
var cardCvc = elements.create('cardCvc', {
style: style
});
cardCvc.mount('#cardCVC');
var cardholderName = $('#custName').val();
var amount = $('#amount').val();
$(document).ready(function () {
$("#paymentForm").submit(function (event) {
//event.preventDefault();
stripe.createPaymentMethod('card', cardNumber, {
billing_details: {name: cardholderName.value}
}).then(function (result) {
console.log(result);
if (result.error) {
var errorElement = document.getElementById('card-error');
errorElement.textContent = result.error.massage;
} else {
stripeTokenHandler(result);
fetch('example.com/stripe/index.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
payment_method_id: result.paymentMethod.id
})
}).then(function (result) {
// Handle server response (see Step 3)
result.json().then(function (result) {
handleServerResponse(result);
})
});
}
});
//return false;
});
function stripeTokenHandler(result) {
var payForm = $("#paymentForm");
var paymentMethodID = result.paymentMethod.id;
//set the token into the form hidden input to make payment
payForm.append("<input type='hidden' name='payment_method_id' value='" + paymentMethodID + "' />");
// payForm.submit();
payForm.submit();
}
}
My Index.php File
header('Content-Type: application/json');
if(isset($_POST['submit'])){
//include Stripe PHP library
require_once('stripe-php/init.php');
//set Stripe Secret Key
\Stripe\Stripe::setApiKey('sk_test_key');
//add customer to stripe
$customer = \Stripe\Customer::create(array(
'email' => $custEmail,
));
function generatePaymentResponse($intent) {
if ($intent->status == 'requires_action' &&
$intent->next_action->type == 'use_stripe_sdk') {
# Tell the client to handle the action
echo json_encode([
'requires_action' => true,
'payment_intent_client_secret' => $intent->client_secret
]);
} else if ($intent->status == 'succeeded') {
# The payment didn’t need any additional actions and completed!
# Handle post-payment fulfillment
echo json_encode([
'success' => true
]);
} else {
# Invalid status
http_response_code(500);
echo json_encode(['error' => 'Invalid PaymentIntent status']);
}
}
# retrieve json from POST body
$json_str = file_get_contents('php://input');
$json1 = json_encode($_POST);
$json_obj = json_decode($json1);
$intent = null;
try {
if (isset($json_obj->payment_method_id)) {
$intent = \Stripe\PaymentIntent::create([
'payment_method' => $json_obj->payment_method_id,
'customer' => $customer->id,
'amount' => 1099,
'currency' => 'gbp',
'confirmation_method' => 'manual',
'confirm' => true,
]);
}
generatePaymentResponse($intent);
}catch (\Stripe\Error\Base $e) {
# Display error on client
echo json_encode([
'error' => $e->getMessage()
]);
}
}
?>
As it can be seen my stripeTokenHandler is appending the payment_method.id into HTML form and the process goes on. but the "fetch" section of JS code should get payment_method_id to generate "Response" and to proceed for "next_action" if the payment status is "requires_action".
So, In order to achieve what i wanted to, what i did was
- removed stripeTokenHandler()
Because i was using it in my previous charges API integration and thought it will work with the new PaymentIntent. And i guess many people misunderstood or misguided buy bunch of different methods stripe has in its docs.I saw a lot of questions on internet people complaining that stripe's "poorly managed" Documentation has confused them.
- Learned Fetch Api.
As a newbie i didnt know what it was for.
- removed isset post submit from my php code
reason: payment.js was unable to POST the paymentMethod.id to the server using Fetch Api and get the response body back from the server to proceed the code further.
I MUST say that Stripe needs to improve its Docs about this SCA ready PaymentIntent thing.
I'm trying to add a stripe checkout button to my Leadpages landing page and after somebody completes a successful payment they're supposed to be redirected...but that redirect is not happening and I have no idea why.
Here's my page: http://snapstories.leadpages.co/test/... it's using test keys right now so you can test the checkout with Stripe's demo Visa number: 4242424242424242 and any expiry / security code...you'll see that you don't get redirected anywhere.
The demo-stripe.php script is supposed to send a 'success' response to my front-end code which triggers the redirect but that 'success' response is not being sent.
Here's the demo-stripe.php code:
<?php
require_once('./stripe/init.php');
$stripe = array(
"secret_key" => "sk_test_******",
"publishable_key" => "pk_test_******"
);
\Stripe\Stripe::setApiKey($stripe['secret_key']);
// Get the credit card details submitted by the form
$token = $_GET['stripeToken'];
$email = $_GET['stripeEmail'];
$callback = $_GET['callback'];
try {
$customer = \Stripe\Customer::create(array(
"source" => $token,
"email" => $email
));
$charge = \Stripe\Charge::create(array(
'customer' => $customer->id,
'amount' => 100,
'currency' => 'usd'
));
header('Content-type: application/json');
$response_array['status'] = 'success';
echo $callback.'('.json_encode($response_array).')';
return 1;
}
catch ( \Stripe\Error\Card $e) {
// Since it's a decline, \Stripe\Error\Card will be caught
}
?>
Here's the front-end code:
<script src="https://checkout.stripe.com/checkout.js"></script>
<script>
var handler = StripeCheckout.configure({
key: 'pk_test_*****',
image: 'imagefile.png',
locale: 'auto',
token: function(token) {
$.ajax({
type: "POST",
dataType: 'jsonp',
url: "https://snapstories.co/demo-stripe.php",
data: { stripeToken: token.id, stripeEmail: token.email},
success: function(data) {
window.location.href = "http//www.google.com";
},
});
}
});
document.getElementsByClassName('w-f73e4cf1-859d-e3e4-97af-8efccae7644a')[0].addEventListener('click', function(e) {
// Open Checkout with further options:
handler.open({
name: 'Testing',
description: 'testing',
amount: 100
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
I'm guessing your front-end code doesn't get to the success function.
Web console returns:
ReferenceError: $ is not defined
It looks like you're using the jQuery command $.ajax(), but I can't see where you've loaded the jQuery library. Try and load it above the script that uses it and see what happens
Be sure to double check the Stripe Checkout requirements. It seems, based on the link you posted, that you're using the HTTP protocol. Stripe Checkout requires you use the HTTPS protocol. That means if you're not using an ssl certificate on your page using Checkout, your page isn't going to return a token nor will it execute any further.