I did implement Payment intents on my website and now works perfectly with this testing card 4242 4242 4242 4242, but for other cards that need 3d secure methods, I take this error "Invalid PaymentIntent status".
the Code that I have used is the same standard code that exists on the Stripe documentation-flow enriched with some code to manage mysql, emails, metadata etc.
Where do I go wrong? Thanks in Advance.
simplified js code connected to index.php
var stripe = Stripe('pk_test_xxx');
var elements = stripe.elements();
var cardElement = elements.create('card', {style: style});
cardElement.mount('#card-element');
var cardholderName = document.getElementById('cardholder-name');
var cardButton = document.getElementById('card-button');
var amount = $('#amount').val();
cardButton.addEventListener('click', function(ev) {
ev.preventDefault();
stripe.createPaymentMethod('card', cardElement, {
billing_details: {name: cardholderName.value}
}).then(function(result) {
if (result.error) {
} else {
$body.addClass("loading");
fetch('https://test.com/server.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payment_method_id: result.paymentMethod.id,
amount: amount
})
}).then(function(result) {
// Handle server response (see Step 3)
result.json().then(function(json) {
handleServerResponse(json);
})
});
}
});
});
function handleServerResponse(response) {
if (response.error) {
} else if (response.requires_action) {
stripe.handleCardAction(
response.payment_intent_client_secret
).then(function(result) {
if (result.error) {
} else {
// The card action has been handled
// The PaymentIntent can be confirmed again on the server
fetch('https://test.com/server.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payment_method_id: result.paymentMethod.id,
amount: amount
})
}).then(function(confirmResult) {
console.log(confirmResult);
return confirmResult.json();
}).then(handleServerResponse);
}
});
} else {
}
}
simplified code on server.php
<?php
# vendor using composer
require_once('stripe6400/init.php');
\Stripe\Stripe::setApiKey('sk_test_xxx');
header('Content-Type: application/json');
# retrieve json from POST body
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str);
$paymentid = $json_obj->payment_method_id;
$amount = $json_obj->amount;
$intent = null;
try {
if (isset($json_obj->payment_method_id)) {
# Create the PaymentIntent
$intent = \Stripe\PaymentIntent::create([
'payment_method' => $json_obj->payment_method_id,
'amount' => $json_obj->amount,
'payment_method_types' => ["card"],
'currency' => 'gbp',
'confirmation_method' => 'manual',
'confirm' => true,
]);
}
if (isset($json_obj->payment_intent_id)) {
$intent = \Stripe\PaymentIntent::retrieve(
$json_obj->payment_intent_id
);
$intent->confirm();
}
generatePaymentResponse($intent);
} catch (\Stripe\Error\Base $e) {
# Display error on client
echo json_encode([
'error' => $e->getMessage()
]);
}
function generatePaymentResponse($intent) {
if ($intent->status == 'requires_action' &&
$intent->next_action->type == 'use_stripe_sdk') {
echo json_encode([
'requires_action' => true,
'payment_intent_client_secret' => $intent->client_secret
]);
} else if ($intent->status == 'succeeded') {
Stripe\Customer::create([
"email" => $email,
"name" => $customer_name,
"source" => "tok_visa" // obtained with Stripe.js
]);
echo json_encode([
"success" => true
]);
} else {
# Invalid status
http_response_code(500);
echo json_encode(['error' => 'Invalid PaymentIntent status']);
}
}
?>
It looks like you might have the same error I just had. The status of the response from stripe is requires_source_action not requires_action so your if statement falls through to Invalid PaymentIntent status.
// change this
// $intent->status == 'requires_action'
// to this
$intent->status == 'requires_source_action'
In my case I'm checking for both so my code is ready for when I do update the stripe SDK.
https://stripe.com/docs/payments/payment-intents/quickstart#confirm-again
(line 33 in the code)
Also on Customer::create your source attribute "tok_visa" must be a real token.id from create token in javascript https://stripe.com/docs/stripe-js/reference#stripe-create-token
Related
I'm playing with the Stripe API. Everything works great, but I want to show possible configurations fails in the frontend. Those fails are handled as an 500.
So this is the condensed code
JS
async function initialize() {
const { clientSecret } = await fetch("index.php?format=json", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id: myid, lang: mylang }),
}).then((r) => {
if (r.status >= 400 && r.status < 600) {
// throw new Error(r.status);
}
return r.json()
}
).catch((error) => {
console.log(error)
}
);
console.log(clientSecret);
}
PHP
header('Content-Type: application/json');
try {
// code doing some stuff ...
$output = [
'secret' => 12345
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
When having a misconfiguration, this is (for example) what the fetch call outputs:
{
"error": true,
"code": 0,
"message": "The payment method type provided: acss_debit is invalid. This payment method is available to Stripe accounts in CA and US and your Stripe account is in XX.",
}
So how can I catch that error message in JS and display it to the user?
I tried all kind of stuff in the initialize function.
I managed to access the catched error message by doing this:
if (r.status >= 400 && r.status < 600) {
return r.text()
.then((text) => {
throw (JSON.parse(text));
});
}
my task is I need to call this API in two different way mean if the first screen comes when I click on submit this API will execute finalCallForInitiate1 otherwise finalCallForInitiate2 this will execute need to set this both API after finalCallForInitiate(); this API so you can see onClick code below both API not execute at the same time need to set according to the screen.
First API with different body
const finalCallForInitiate1 = () => {
let body;
body = {
screening : "single Screening",
timestamp: props.ts,
};
const config = {
headers: {
"Content-Type": "application/json",
},
};
axios
.post(`${DJANGO_SERVER_ADDRESS}/data/initiate/`, body, config)
.then(
(res) => {
console.log(".......................", res);
},
(err) => {
}
);
};
Same API with different body
const finalCallForInitiate2 = () => {
let body;
body = {
screening : "dual Screening",
timestamp: props.ts,
};
const config = {
headers: {
"Content-Type": "application/json",
},
};
axios
.post(`${DJANGO_SERVER_ADDRESS}/data/initiate/`, body, config)
.then(
(res) => {
console.log(".......................", res);
},
(err) => {
}
);
};
onClick={(e) => {
if (formData.alert == false && formData.non_alert == false) {
Swal.fire({
text: `Select Atleast One Checkbox`,
icon: "warning",
confirmButtonColor: "#0BB7A7",
});
// analystcall();
} else {
finalCallForInitiate();
}
finalCallForInitiate1()
finalCallForInitiate2()
}}
If you body data is only changing then make single function and pass changed body data as arguments.
In above example do like this.
const finalCallForInitiate1 = (screening) => {
let body;
body = {
screening,
timestamp: props.ts,
};
const config = {
headers: {
"Content-Type": "application/json",
},
};
axios
.post(`${DJANGO_SERVER_ADDRESS}/data/initiate/`, body, config)
.then(
(res) => {
console.log(".......................", res);
},
(err) => {
}
);
};
onClick={(e) => {
if (formData.alert == false && formData.non_alert == false) {
Swal.fire({
text: `Select Atleast One Checkbox`,
icon: "warning",
confirmButtonColor: "#0BB7A7",
});
// analystcall();
} else {
finalCallForInitiate();
}
finalCallForInitiate1('single Screening');
finalCallForInitiate1('dual Screening');
}}
The way you explain your need seems slightly bafouting. But for what I understand you have an API built with
Django
and want to proceed data. The
'POST'
Method used means that you are forwarding data. You also want to proceed conditional rendering. For that you likely have to review your API architecture and check whether the same API URL provided for the fetch() function can reliably proceed data for 2 different data types. All this is to be taken into consideration.
The problem is more about API architecture.
May be you will need to give us more details about it. I would have just made a comment but I do not have enought stackoverflow-based reputation to comment, so I post this as a guide.
i built a registration form and validated it using javaScript. but i want after a user filled the form, it should post to the server. i am confused on where to place my httpRequest function. i don't know if its after validation or inside the validation function
This is my validation function
function formregister(e){
if (first_name.value=== "" || last_name.value=== "" || user_id.value==="" || id_type.value=== ""
|| id_no.value=== "" || address.value==="" || !terms.checked) {
var fPassResult = '1';
} else{
var fPassResult = '0';
}
if(fPassResult === "1") {
window.location = "register.html";
}else{
Swal.fire({
type: 'success',
title: 'Your Registration has been Submitted Successfully',
text: 'Click Ok to Login',
timer: 10000
}
).then(function(){
window.location="Login.html";
})
}
e.preventDefault();
};
**And this is my post request function**
function registerationApiCall(e){
var data = {
"user_id":"user_id.value",
"user_pin": "user_pin.value",
"first_name":"first_name.value",
"last_name":"last_name.value",
"address":"address.value",
};
fetch("jehvah/api",{
type : 'POST',
data : data,
dataType : 'JSON',
encode : true,
success: function (response, status, xhr) {
if (result==="OK") {
console.log("success");
}else{
console.log("bad");
}
},
error: function (xhr, status, error) {
console.log("something went wrong");
}
});
}
Please kindly check my post request function, i dont know if i am doing it the right way
Hi ✌ when fPassResult === "0" in this case inside else{} call registerationApiCall()
you tell the user it's a success after you get OK from the server which is Asynchronous call
& inside fetch response you call swal.fire
for this code to work your server when checks the database & every thing is ok returns a msg like this {"msg":"OK"}
CODE:
else{
registerationApiCall()
}
function registerationApiCall becomes
fetch('jehvah/api',
{ method: 'POST',headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data)})
.then((response) => response.json())
.then((result) => {
console.log('Success:', result);
if (result.msg="OK") {
console.log("success");
Swal.fire({
type: 'success',
title: 'Your Registration has been Submitted Successfully',
text: 'Click Ok to Login',
timer: 10000
}).then(function(){window.location="Login.html";})
}else{ console.log("usres exsists / etc");}
})
.catch((error) => {
console.log("something went wrong");
});
}
Also in the request payload you sent a group of strings not the variables containing the form values
Here
var data = {
"user_id":"user_id.value",
"user_pin": "user_pin.value",
"first_name":"first_name.value",
"last_name":"last_name.value",
"address":"address.value",
};
Change that to
var data = {
"user_id":user_id.value,
"user_pin": user_pin.value,
"first_name":first_name.value,
"last_name":last_name.value,
"address":address.value
};
Looking at the fetch documentation (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch), success and error does not exists.
The fetch function returns a Promise, so you can handle results as any Promise should do:
fetch("jehvah/api", {
method: 'POST',
body : JSON.stringify(myDataObject)
})
.then(blob => blob .json())
.then(result => console.log('Success!!))
.catch(e => console.log('Failure :('))
I'm trying to send data to the server API from a webpage but it keeps falling to
'No Connection!' as you will see in the code.
Note:The server, database and the API are working, because I also use it on a phone application that do the same as I'm trying to do here which is post an event to the database.
Here is the webpage code:
function onAddEvent(){
var title = document.getElementById("title").value;
var desc = document.getElementById("desc").value;
var date = document.getElementById("date").value;
var userid = localStorage.getItem("userid");
$.ajax({
url: API_URL,
type: 'POST',
data: {eventname: title, eventdate: date, eventdesc: desc, user_id: userid},
async: true, // set the property here
success: function(data) {
if(data.result == "success"){
alert("Add Event Successfully!");
}
else{
alert("Can't add event");
}
},
error: function(xhr, error) {
//It is falling here
alert('No Connection!');
}
});
}
And here is the PHP API that it will connect to:
function addevent()
{
$new_member_insert_data = array(
'eventname' => $this->input->post('eventname'),
'eventdate' => $this->input->post('eventdate'),
'eventdesc' => $this->input->post('eventdesc'),
'user_id' => $this->input->post('user_id')
);
$insert = $this->db->insert('event', $new_member_insert_data);
return $insert;
}
Remove the code from the function or try calling the function in the API.
//Call the function from your API
addevent();
function addevent()
{
$new_member_insert_data = array(
'eventname' => $this->input->post('eventname'),
'eventdate' => $this->input->post('eventdate'),
'eventdesc' => $this->input->post('eventdesc'),
'user_id' => $this->input->post('user_id')
);
$insert = $this->db->insert('event', $new_member_insert_data);
return $insert;
}
So I want to use an sms service from 46elks in my meteor project. The following php script allows you to send an sms:
<?
// Example to send SMS using the 46elks service
// Change $username, $password and the mobile number to send to
function sendSMS ($sms) {
// Set your 46elks API username and API password here
// You can find them at https://dashboard.46elks.com/
$username = 'u2c11ef65b429a8e16ccb1f960d02c734';
$password = 'C0ACCEEC0FAFE879189DD5D57F6EC348';
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => "Authorization: Basic ".
base64_encode($username.':'.$password). "\r\n".
"Content-type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query($sms),
'timeout' => 10
)));
return false !== file_get_contents(
'https://api.46elks.com/a1/SMS', false, $context );
}
$sms = array(
'from' => 'DummyFrom', /* Can be up to 11 alphanumeric characters */
'to' => '+46400000000', /* The mobile number you want to send to */
'message' => 'Hello hello!'
);
sendSMS ($sms);
?>
Now I need this in my meteor project and I've been trying to convert it to meteors http.call():
HTTP.call("POST", "https://api.46elks.com/a1/SMS", {
headers:
{
"Authorization": "Basic SomeLongBase46EncodedString",
"Content-type": "application/x-www-form-urlencoded"
},
data:
{
"from": "testFrom",
"to": "+46701111111",
"message": "test message"
}
},
function (error, result)
{
if (error)
{
console.log("error: " + error);
}
else
{
console.log("result: " + result);
}
});
But what I keep getting is the following error:
error: Error: failed [403] Missing key from
Change data to params:
params: {
"from": "testFrom",
"to": "+46701111111",
"message": "test message"
}
and use auth instead of Authorization (docs):
"auth": username + ":" + password
ok, I had to replace data: { ... } with the following formatted string:
content: "from=testFrom&to=+46701111111&message=test message"
This to convert the php http_build_query() correctly.