I am developing an Ionic 5 app with a PHP backend. I copied the code from the Stripe docs and put it in a try/catch as follows:
\Stripe\Stripe::setApiKey('sk_test_********************');
$postedChargeObject = file_get_contents("php://input");
$cO = json_decode($postedChargeObject);
try {
$charge = \Stripe\Charge::create([
'amount' => $cO->amount,
'currency' => 'usd',
'description' => 'charge test with token 2',
'source' => $cO->token,
]);
echo "success";
} catch(\Stripe\Error\Base $e){
echo($e->getMessage());
} catch (Exception $e){
echo $e;
}
With the echo "success", I get some unexpected behavior. 1) it takes a long time to get any response from my server and 2) I get the following error in the developer console:
If I comment out the echo "success" it still takes a few seconds but the charge goes through because I can see it on my Stripe dashboard.
Ideally, I would like my PHP script to charge the card with Stripe::create and return the charge object, as defined in the Stripe charge object docs, back to the client script. So not just echo "success" but the whole Stripe charge object.
My service for calling the PHP script is:
chargeCardForOrder(chargeObj): Observable<any> {
let c = JSON.stringify(chargeObj);
console.log("stringified c from service", c);
return this.http.post(this.chargeUrl, c);
}
My .ts file that calls the service is:
this.coService.chargeCardForOrder(cO).subscribe( resp => {
console.log( "response from server charge", resp );
})
On a side note, if I take out the echo "success" from the PHP script, the charge goes thru as per my Stripe dashboard but the console reads "response from server charge null". I am quite confused as to what I am missing. Your help would be greatly appreciated.
Your client-side code is expecting a JSON object, and you're sending it just the string success which is not valid JSON. You should likely be responding with JSON in the shape that you client-side code expects, or not returning any content at all if it's not needed client-side.
Related
I am using Stripe's 'Payment' element for creating paymentintent and card charges, Using PHP, HTML & JS.
https://stripe.com/docs/payments/payment-element
a) As soon as i load the payment page, Stripe generates a paymentintent with status 'Incomplete'.
b) After i enter Credit Card details and hit 'pay', Stripe again issues a second paymentintent with status accordingly (say 'Succeeded')
The result is, that my Dashboard is now full of unnecessary records
This is, because i initialize the $paymentIntent = \Stripe\PaymentIntent::create as soon as the page loads.
I understand that this is Stripe default behaviour, because at that moment, no payment_method is attached yet.
My question is: How is this resolved best, to avoid such 'Incomplete' records?
Maybe attach an object and fire the paymentintent creation only when
that object is present?
Or onclick of the 'pay' button..awaiting paymentintent, confirm presence and then submit the form?
Or retrieve that 1st paymentintent, store it and then update with
form submission?
create.php
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' => 1000,
'currency' => 'eur',
'receipt_email' => 'whatever#mail.com',
'automatic_payment_methods' => ['enabled' => true,],
'description' => 'Reservation Dimi123 / Name: John Doe',
'metadata' => ['order_id' => '12345']
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
script.js
const stripe = Stripe("pk_test_..");
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" },
}).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: {
return_url: "https://localhost/stripe/prebuilt-checkout-custom-flow/public/checkout.html",
},
});
if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message);
} else {
showMessage("An unexpected error occurred.");
}
setLoading(false);
}
How to Avoid Incomplete Payment Intents? You don't.
his is how the system is designed to function since you need the client_secret from the Payment Intent (or Setup Intent) to render the Payment Element. There is no negative impact to your account.
You should feel comfortable ignoring them. You can adjust the filters in your Payments tab to not see them. They aren't a great metric of individual customers coming to your payment interface and navigating away since any user refreshing their browser would trigger a new one.
I have a javascript file that captures form input, makes an XMLHTTP POST request with the input, and handles errors. I am currently POSTing to a separate PHP file, as the request requires sensitive API data and needs encoding. From the PHP file, I then send a cURL request to post the form input to the remote URL.
I want to handle errors based on the cURL response, but the XHR errors are different and take precedent over the cURL errors. Are both these requests necessary, or should I only be making either a single XHR or cURL request?
Whether or not both requests are necessary depends on your use case. Since yours sounds as they are indeed necessary, there's absolutely nothing wrong with it.
How you handle server-side errors client-side is also entirely up to you. For example, let's assume this pretty basic XHR handler, which sends some JSON data off to some endpoint and evaluates the response:
var xhr = new XMLHttpRequest();
xhr.open('POST', '/endpoint.php');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = () => {
const status = xhr.status;
const response = JSON.parse(xhr.responseText);
if (status == 200) {
// Everything's fine
console.log(response.data);
} else {
// Some error occured
console.log(status, response.data);
}
};
xhr.send(JSON.stringify({}));
index.html
The above error handling strategy revolves around the HTTP status code received from the server, so we'll need to make sure they're send according to our needs:
/**
* In case an error occurred, send an error response.
*/
function error(string $message) {
// For properly returning a JSON response, see:
// https://stackoverflow.com/a/62834046/3323348
header("Content-type: application/json; charset=utf-8");
http_response_code(400); // HTTP status code - set to whatever code's appropriate
echo json_encode(['data' => $message]);
}
/**
* Send a response denoting a success.
*/
function success(array $data) {
// For properly returning a JSON response, see:
// https://stackoverflow.com/a/62834046/3323348
header("Content-type: application/json; charset=utf-8");
http_response_code(200);
echo json_encode(['data' => $data]);
}
// For proper error checking on JSON data, see:
// https://stackoverflow.com/a/37847337/3323348
$data = json_decode(file_get_contents('php://input'), true);
// Assume some processing happened. That result is either
// a success, or an error.
$success = mt_rand(0,1);
$success ? success($data) : error('An error occured.');
endpoint.php
Regarding cURL, you could easily change the last two lines to e.g.:
if(curl_errno($ch)) { // Assuming $ch is your cURL handle
error('Curl error: '.curl_error($ch));
}
else {
success($data);
}
Or you adapt the error function - if it would accept an array instead of a simple string, you'd be able to return more complex error data to your JavaScript client:
$curl_error = curl_errno($ch);
if ($curl_error) {
error([
'type' => 'cURL',
'no' => curl_errno($ch),
'message' => curl_error($ch)
]);
}
You don't have to stick with status codes for your error handling, though. You could send a JSON object back with one key denoting some successfully collected data, and one denoting an error if an error occurred. Or whatever suits your needs.
I have configured webpush and all is going well... I have registered the service-worker.js, can request permission, save the subscription to my db and send a push notification from the server using the library which is installed.
When I send the push notification
[
'subscription' => Subscription::create($subs_arr),
'payload' => '{"title":"test title","msg":"Hello World!", "icon":"https://www.newhomesforsale.co.uk/media/chevron.png","url":"https://www.newhomesforsale.co.uk/"}'
]
];
I get a success message:
[v] Message sent successfully for subscription https://fcm.googleapis.com/fcm/send/e2JHJ2YcIfM:APA91bHwU7CruFTDkpAH-zbnJRNhvJEK-mCze2hFNa48mdK8pk-oWuXJUn57Ai9Nw0d-skviCfJ40g1yX7qWKucGHPF3jeNyhkJfZ-8kpxYJNQowrAR561b0dQZJAseL_eBsJRMrxnDP.
and a push message appears in the browser - great.
The problem I have is that the service-worker file doesn't seem to see the payload information because the message displays even as simply
Oh No - no data {"isTrusted": true}
Service-worker file:
self.addEventListener('push', function(event) {
if (!(self.Notification && self.Notification.permission === 'granted')) {
return;
}
const sendNotification = body => {
// you could refresh a notification badge here with postMessage API
const title = "Web Push example";
return self.registration.showNotification(title, {
body,
});
};
if (event.data) {
const message = event.data.text();
event.waitUntil(sendNotification(message));
} else {
do_serverlog('Push event but no data');
sendNotification("Oh No - no data" + JSON.stringify(event));
}
});
Would be great to get the last piece of the puzzle to figure out how to correctly send/read the payload.
I have found that I made a mistake passing throught the keys into the subscription ($subs_arr).
It seems that if you have provide the endpoint, but not the keys, the push notification still works, but with the data stripped out. This sidetracked me as I had assumed that it wouldn't work at all if the keys were missing.
I thought maybe this might help someone at some point.
I've a php script who manage the connection of a user. When the connection is accepted by the script, a JSON web token is sent to the web service. The problem is, when I test my connection API with this web service, the script send me nothing without error. However, when i test it with postman, the script send me that token.
Here is my PHP script :
//Now create the TOKEN
$jwt = JWT::encode(
$data,
$secret_key,
$algorithme
);
$unencoded_array = ['jwt' => $jwt, 'admin' => $admin];
//echo "test";
echo json_encode($unencoded_array);
And here is my client call :
$rootScope.Connect = () => {
//Set params to request
var request = Global.url_api+'action=Connection&username='+$scope.username+
'&password='+$scope.pwd+
'&project='+$rootScope.project;
//Ask to the server if credentials is correct
$http.get(request)
.then((response) => {
console.log(response)
}
BUT, when i uncomment this
echo "test";
I receive my token with that string as you can see here :
Any idea ? if you need more information, please don't hesitate to ask.
I am using Stripe elements to make an asychronous paymentRequest to charge customers. The request returns 'success' irrespective of the charge result so what's the best way to return a state so I can handle the charge state client side and report a failed charge? Do I need to use endpoints? Stripe seems to leave us hanging after the charge with no guidance.
Listener:
<script>
paymentRequest.on('token', function(ev) {
fetch('https://example.com/pub/apple.php', {
method: 'POST',
body: JSON.stringify({token: ev.token.id , email: ev.token.email}),
headers: {'content-type': 'application/json'},
})
.then(function(response) {
if (response.ok) {
// Report to the browser that the payment was successful, prompting
// it to close the browser payment interface.
ev.complete('success');
} else {
// Report to the browser that the payment failed, prompting it to
// re-show the payment interface, or show an error message and close
// the payment interface.
ev.complete('fail');
}
});
});
</script>
https://example.com/pub/apple.php:
require_once('../_stripe-php-4.9.0/init.php');
// Retrieve the request's body and parse it as JSON
$input = #file_get_contents("php://input");
$json = json_decode($input);
// get the token from the returned object
$token = $json->token;
$email = $json->email;
$skey = 'sk_test_Y********************F';
\Stripe\Stripe::setApiKey($skey);
// create the customer
$customer = \Stripe\Customer::create(array(
"source" => $token,
"description" => 'Device',
"email" => $email)
);
//charge the card
$charge = \Stripe\Charge::create(array(
"amount" => 1000,
"currency" => 'GBP',
"statement_descriptor" => 'MTL',
"description" => 'Device - '.$customer->id,
"customer" => $customer->id)
);
In order to let the user know that their charge failed, and trigger ev.complete('fail'); in the code above, you'll need response.ok inside your fetch request to return false.
Let's first look at the definition of that response.ok property,
The ok read-only property of the Response interface contains a Boolean
stating whether the response was successful (status in the range
200-299) or not.
via https://developer.mozilla.org/en-US/docs/Web/API/Response/ok
So for a successful request you want PHP to return a 2xx status (let's say 200) , and for failure a non-200 HTTP status code (lets say a 402).
In your PHP, you will attempt to make a Charge, then receive a response from Stripe. Based upon this response, you should make PHP return a status to your front-end --- either a 200 OK or a 402 error.
You can do this with the function http_response_code(), e.g. http_response_code(200) or http_response_code(402)
http://php.net/manual/en/function.http-response-code.php
Let's look at a simplified example
try {
// make a charge
$charge = \Stripe\Charge::create(array(
"amount" => 1000,
"currency" => 'GBP',
"source" => $token
));
// send a 200 ok
http_response_code(200);
print("Charge successful");
} catch(\Stripe\Error\Card $e) {
// Since it's a decline, \Stripe\Error\Card will be caught
$body = $e->getJsonBody();
$err = $body['error'];
// this charge declined, return a 402
// response.ok should then be false
http_response_code(402);
print('Error:' . $err['message']);
}
The Charge call here is wrapped in a try-catch block. If the charge succeeds, a 200 HTTP response code is sent, and response.ok will be true.
If the user provides a card that declines, that error will be caught, and 402 HTTP response code will be returned (along with an error message). In that case response.ok would be false, so ev.complete('fail'); would be called.
There are some other types of Stripe errors you probably want to catch, a full reference is here,
https://stripe.com/docs/api/errors
https://stripe.com/docs/api/errors/handling