I am setting up a small Slim 3 application which will be the middleman for processing Web based Stripe payment form. The Slim application will handle the business logic of reaching out to Stripe and conducting the payment, as well as sending the payment data off to some third-party CRM / Applications.
My issue during testing is that when i make a test payment through the StripeJS payment form the slim application is actually logging 3 duplicate requests 1-second apart. Here's what the logging looks like:
test#gmail.com Attempting to make payment 2020-04-27 05:34:39
test#gmail.com Attempting to make payment 2020-04-27 05:34:40
test#gmail.com Attempting to make payment 2020-04-27 05:34:41
test#gmail.com Payment success 2020-04-27 05:34:44
test#gmail.com Payment success 2020-04-27 05:34:45
test#gmail.com Payment success 2020-04-27 05:34:47
This is all from an individual "payment" using the Stripe Test credentials.
The Slim layout is fairly straightforward in terms of the route passing the request directly to my StripeController class. The StripeController class ( in development ) will take the values of the request and pass it to the Stripe API to conduct the payment.
The route layout:
$this->post('/stripe-payment', 'StripeController:stripePayment');
The controller (stripped to essentials for privacy ):
class StripeController extends Controller
{
private $privateKey;
private $httpClient;
public function __construct() {
$this->privateKey = getenv('STRIPE_KEY');
$this->httpClient = new Client();
\Stripe\Stripe::setApiKey($this->privateKey);
}
//
public function stripePayment($request, $response)
{
// Get the request body
$r = (object)$request->getParsedBody();
// Initial Logging
$log = new LogController($r->email_address, 'Attempting to make payment');
$log->saveLog();
// Attempt to find an existing user
$user = User::where('email', $r->email_address)->first();
if($user !== null) {
// If user exists, use their StripeID
$invoice = $this->stripeInvoice($r, $user->stripe_id);
// Log the transaction
$this->savePayment( $user->id, 'stripe-invoice', $invoice->id, $invoice->amount_paid );
// Third party application push
$this->pushUser($r, $invoice->amount_paid);
// Return with success
return $response->withRedirect('example.com/payment-success/');
} else {
// Same as above, just create a user then push.
}
}
public function stripeInvoice(object $r, string $stripe_id)
{
// Try to create the invoice in stripe
try {
\Stripe\InvoiceItem::create([
'customer' => $stripe_id,
'amount' => (int)$r->product,
'currency' => 'USD',
'description' => $plan_desc
]);
} catch ( Exception $e ) {
error_log('STRIPE_INVOICE_EXCEPTION: '.$e->getMessage());
}
// Try to charge the customer the invoice we just created
try {
$invoice = \Stripe\Invoice::create([
'customer' => $stripe_id
]);
$_get_invoice = \Stripe\Invoice::retrieve($invoice->id);
$_get_invoice->pay();
$success = 1;
// Use Stripe's library to make requests...
} catch(\Stripe\Exception\CardException $e) {
$error = $e->getMessage();
// Since it's a decline, \Stripe\Exception\CardException will be caught
} catch (\Stripe\Exception\RateLimitException $e) {
$error = $e->getMessage();
// Too many requests made to the API too quickly
} catch (\Stripe\Exception\InvalidRequestException $e) {
$error = $e->getMessage();
// Invalid parameters were supplied to Stripe's API
} catch (\Stripe\Exception\AuthenticationException $e) {
$error = $e->getMessage();
// Authentication with Stripe's API failed
// (maybe you changed API keys recently)
} catch (\Stripe\Exception\ApiConnectionException $e) {
$error = $e->getMessage();
// Network communication with Stripe failed
} catch (\Stripe\Exception\ApiErrorException $e) {
$error = $e->getMessage();
// Display a very generic error to the user, and maybe send
// yourself an email
} catch (Exception $e) {
// Something else happened, completely unrelated to Stripe
$error = $e->getMessage();
}
if($success != 1) {
return $response->withRedirect('example.com&error='.$error);
} else {
return $_get_invoice;
}
}
}
Is there anything that Slim is doing in the background, like an origin check, that could be causing the duplication issue ?
Related
I has create a webhook php to trigger the paymentintent event status. I had successfully tigger it but the function of detected event is no working which i just want to redirect to another page if successful or not. I try to echo text, it also not showing. I can see the response body(show the text that i want to echo" in the dashboard of Stripe. Did i misunderstanding the concept of it ? This is my code:
<?php
/*
Template Name: webhook
*/
include_once(get_template_directory_uri().'/customFile/stripe-php-7.71.0/init.php');
\Stripe\Stripe::setApiKey('xxx'); // this is true, i just replace with xxx
$payload = #file_get_contents('php://input');
$event = null;
$paymentstatus = "Payment Failed";
try {
$event = \Stripe\Event::constructFrom(
json_decode($payload, true)
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
http_response_code(400);
exit();
}
// Handle the event
switch ($event->type) {
case 'payment_intent.succeeded':
$paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent
//handlePaymentIntentSucceeded($paymentIntent);
echo"Success";
echo "<script>location.href='http://localhost/wordpress/';</script>";
break;
case 'payment_intent.payment_failed':
$paymentMethod = $event->data->object; // contains a \Stripe\PaymentMethod
//handlePaymentMethodAttached($paymentMethod);
echo "<script>location.href='http://localhost/wordpress/shop/';</script>";
break;
}
http_response_code(200);
Webhooks are requests that come from Stripe to your web server. There's no user attached to these events so trying to redirect after you receive a webhook event makes no sense. All that's happening is you're sending a <script> string back to Stripe, who promptly ignores it because all they are about is the response status code.
If you want to redirect your user after they've made a successful payment, you should either that on the client after they've confirmed the PaymentIntent or in your success_url if you're using Checkout.
i have integrated coinbase api in my web app. When charge is created, users are directed to coinbase commerce website to make payment. How do i check if the user has finished paying or not and if the exact amount has been paid. Below is my code
<?php
require_once __DIR__ . "/vendor/autoload.php";
use CoinbaseCommerce\ApiClient;
use CoinbaseCommerce\Resources\Charge;
/**
* Init ApiClient with your Api Key
* Your Api Keys are available in the Coinbase Commerce Dashboard.
* Make sure you don't store your API Key in your source code!
*/
ApiClient::init("MY API KEY HERE");
$chargeObj = new Charge();
$chargeObj->name = 'Bitcoin Deposit';
$chargeObj->description = 'Testing the payment system';
$chargeObj->local_price = [
'amount' => '100.00',
'currency' => 'USD'
];
$chargeObj->pricing_type = 'fixed_price';
try {
$chargeObj->save();
// insert into database with status pending
$queryobject->insertTransaction($_SESSION['user_id'],$chargeObj->id, $amount, $status, $currentTime,
$chargeObj->name, $chargeObj->currency);
} catch (\Exception $exception) {
echo sprintf("Sorry! payment could not be created. Error: %s \n", $exception->getMessage());
}
if ($chargeObj->id) {
$chargeObj->description = "New description";
// Retrieve charge by "id"
try {
$retrievedCharge = Charge::retrieve($chargeObj->id);
$hosted_url = $retrievedCharge->hosted_url;
header('location: '.$hosted_url);
} catch (\Exception $exception) {
echo sprintf("Enable to retrieve charge. Error: %s \n", $exception->getMessage());
}
}
You need to use webhooks for this, you can create an endpoint for this. Unfortunately Coinbase does not have a sandbox environment so you are going to need a bit of cryptocurrency in your account.
Need assistance with Stripe error response.
Everything seems to work according to stripe dashboard logs when creating a Customer and then enrolling them into a Subscription therefore generating a Charge, in the event where there are multiple requests made with idempotency keys in place.
How ever in this event I get this stripe error response (capture of the exception $error6) which opens up as a page (charge.php) where the code runs, instead of sending to success page.
charge.php
\Stripe\Stripe::setApiKey('sk_live_xxxxxxxxxxx');
$POST = filter_var_array($_POST, FILTER_SANITIZE_STRING);
$email = $POST['email'];
$token = $POST['stripeToken'];
$membership_type = $POST['membership_type'];
$user_id = $POST['user_id'];
$success = 0;
try {
// Create customer in Stripe
$customer = \Stripe\Customer::create([
"email" => $email,
"source" => $token,
],[
"idempotency_key" => $_SESSION['sid2'],
]);
$success = 1;
} catch(Stripe_CardError $e) {
$error1 = $e->getMessage();
} catch (Stripe_InvalidRequestError $e) {
// Invalid parameters were supplied to Stripe's API
$error2 = $e->getMessage();
} catch (Stripe_AuthenticationError $e) {
// Authentication with Stripe's API failed
$error3 = $e->getMessage();
} catch (Stripe_ApiConnectionError $e) {
// Network communication with Stripe failed
$error4 = $e->getMessage();
} catch (Stripe_Error $e) {
// Display a very generic error to the user, and maybe send
// yourself an email
$error5 = $e->getMessage();
} catch (Exception $e) {
// Something else happened, completely unrelated to Stripe
$error6 = $e->getMessage();
}
if ($success!=1)
{
$_SESSION['error1'] = $error1;
$_SESSION['error2'] = $error2;
$_SESSION['error3'] = $error3;
$_SESSION['error4'] = $error4;
$_SESSION['error5'] = $error5;
$_SESSION['error6'] = $error6;
print_r($_SESSION);
}
// Add Customer to a Subscription in Stripe
$subscription = \Stripe\Subscription::create([
'customer' => $customer->id,
'items' => [['plan' => $membership_type]]
],[
"idempotency_key" => $_SESSION['sid'],
]);
//adding all relevent info into data base...
//send user to success page
header('Location: ../success.php?id='.$user_id.'&product='.$subscription->plan->nickname);
Could this be because each time the Stripe JS $token parameter changes? is this normal or am I doing something wrong? (I got a similar error when running idempotency only on Subscribing a Customer but then stripe creates multiple customers with the same email and payment cards but different customer->id)
Can anyone kindly suggest how can I resolve this error page?
Could this be because each time the Stripe JS $token parameter changes?
Yes, I suspect what's happening here is that you are reusing $_SESSION['sid2'] for two separate requests with different source parameters to create a Customer. And this error in response is the expected behavior!
You should be able to see this in your Dashboard logs: Assuming this is a test mode request, https://dashboard.stripe.com/test/logs/iar_IgylJRGpbLyVb6 should tell you where the same key was originally used.
best option i could find is disabling the button after the Stripe JS event listener
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
$('.button').attr("disabled", true);
stripe.createToken(card).then(function(result) {...........
In Stripe, the test number 4000 0000 0000 0341 simulates, per the Stripe Testing page, a situation where "Attaching this card to a Customer object succeeds, but attempts to charge the customer fail." In my case, I want to treat this situation like an error and send it to the error handler rather than report to the customer that the charge succeeded.
I'm doing this in PHP, so I'm not getting the JSON object from the API but rather a PHP object for the customer. I'm new to the Stripe API so I don't know what else I should be doing here. I tried searching for info about this situation and couldn't find anything helpful. And it doesn't look like this case was handled by existing SO questions.
The excerpt from the code is below. I need to set $charge_failed to true if the customer was not charged.
\Stripe\Stripe::setApiKey(STRIPE_SECRET_KEY);
// Create a customer.
try {
$customer = \Stripe\Customer::create(array( "source" => $my_token, 'email' => $customers_email, "description" => $my_description ));
$charge_failed = false; //TODO Set this boolean according to whether the charge passed or failed.
if ($charge_failed) {
//TODO Report to the user that the charge failed and they need to resubmit.
die();
}
}
catch(\Stripe\Error\Card $e) {
// Card was declined. Report the card declined to the user.
die();
}
catch (\Stripe\Error\RateLimit $e) {
// Report the error appropriately.
die();
} catch (\Stripe\Error\InvalidRequest $e) {
// Report the error appropriately.
die();
} catch (\Stripe\Error\Authentication $e) {
// Report the error appropriately.
die();
} catch (\Stripe\Error\ApiConnection $e) {
// Report the error appropriately.
die();
} catch (\Stripe\Error\Base $e) {
// Report the error appropriately.
die();
} catch (Exception $e) {
// Report the error appropriately.
die();
}
Before some days I implemented stripe successfully. Check below hope this will help.
if ($params['testmode'] == "on") {
\Stripe\Stripe::setApiKey($params['private_test_key']);
$pubkey = $params['public_test_key'];
} else {
\Stripe\Stripe::setApiKey($params['private_live_key']);
$pubkey = $params['public_live_key'];
}
include_once 'Stripe/init.php';
include_once 'Stripe/lib/Stripe.php';
//Set Stripe keys
$params = array(
"testmode" => "off",
"private_live_key" => "sk_live_",
"public_live_key" => "pk_live_",
"private_test_key" => "sk_test_",
"public_test_key" => "pk_test_"
);
if ($params['testmode'] == "on") {
\Stripe\Stripe::setApiKey($params['private_test_key']);
$pubkey = $params['public_test_key'];
} else {
\Stripe\Stripe::setApiKey($params['private_live_key']);
$pubkey = $params['public_live_key'];
}
if (isset($_POST['stripeToken'])) {
try {
$customer = \Stripe\Customer::create(array(
'email' => $varUserEmail,
'source' => $_POST['stripeToken']
));
$charge = \Stripe\Charge::create(array(
'customer' => $customer->id,
'amount' => $price,
'currency' => 'usd',
'description' => $description,
));
$result = "success";
//Save information in the database and send an email with status
//Ends here
} catch (\Stripe\Error\Card $e) {
$error = $e->getMessage();
$result = "declined";
//Save information in the database and send an email with status
}
I could be wrong but I believe that this situation occurs only if you already have an existing customer object and you are attempting to update their payment method. Your token you are using to create a customer will be invalid prior to attempting the customer creation and customer creation will fail. If this is an initial charge, the javascript method will return a failure.
Ok... I have customer bob. His user id is 1.
Bob is already a customer in Stripe and he already has a subscription.
I want to cancel his subscription so I do this:
$sub = \Stripe\Subscription::retrieve($subscriptionId);
$sub->cancel();
that works.
now I want to resubscribe him to a plan (doesn't matter which plan, any paid plan)... I put it in a try catch: so I do this:
try {
$subscription = \Stripe\Subscription::create(array(
"customer" => $customerId, // bob's id is 1
"plan" => "premium-plan"
));
} catch (\Stripe\Error\ApiConnection $e) {
} catch (\Stripe\Error\InvalidRequest $e) {
// Error is here
} catch (\Stripe\Error\Api $e) {
} catch (\Stripe\Error\Card $e) {
}
the error I get is here: \Stripe\Error\InvalidRequest $e
what could be wrong?